Is your 401k bleeding money by not offering low-cost index funds? Now there is a way to find out.
GreaterThanZero.com


Page 11 of: C++ auto and decltype Explained, by Thomas Becker   about me  

Summary and Epilogue

We have learned that C++11 has four different ways of making the type of an expression available to the programmer:
  • auto can be used to set the type of a newly declared variable from its initializing expression. It removes the reference, if any, from the expression's type, then removes topmost const and volatile qualifiers.
  • decltype can be used in a wider variety of contexts, such as typedefs, function return types, and even in places where a class name is expected. There are two different ways in which decltype(expr) can work, depending on the nature of expr:
    • If expr is something as simple as a plain variable whose type can be looked up in the source code, then decltype(expr) is that type.
    • Otherwise, decltype(expr) is the type of expr with an extra lvalue reference added for lvalues and an extra rvalue reference added for xvalues.
  • decltype(auto) is an alternate way of setting the type of a newly declared variable from its initializing expression. It uses the type deduction rules of decltype instead of those of auto.

If you focus on the the way references are treated in the above, you see that auto always drops an outermost reference, whereas decltype either preserves it or even adds one, depending on the circumstances.

At the end of Section 3, I gave you what I think is a plausible explanation of the rationale behind the semantics of auto. The question arises, "What about the rationale behind the semantics of decltype? Why does it work the way it does, specifically with respect to references?" As with auto, there is probably more than one way to argue. From what I have read and heard, the final specification of decltype represents a compromise between two different possible points of view. One point of view is that decltype should be a way to retrieve and reuse the type of a variable as declared in the source code: I declared a variable x of type T at some point in my code. Now I want to use that type for some other purpose, like making a typedef, or specify the return type of a function. I don't want to repeat myself, so give me a way to recover the declared type of my variable, exactly the way I originally wrote it. If that declared type has a reference and/or a const or volatile qualifier on it that I don't want, I'll remove that myself, thank you very much. This is what Case 1 of the specification of decltype does.

The above way of looking at decltype says nothing about what should happen when decltype is applied to more complex expressions. From what we've said so far, decltype might as well be undefined for complex expressions. This is where a different point of view comes in, a point of view that originates in the needs of library writers. They often find themselves in a situation where the return type of a function needs to be the type of some expression, typically something that depends on template parameters. They want to be able to write

auto foo([...]) -> decltype(expr) {
  [...]
  return expr;
}
where expr could be any expression. Moreover, if expr is an lvalue and is not local to the function, they want to return a refefence, so the returned expression can be assigned to. This is particularly important if the function foo is some kind of forwarding function. For that to always work properly, there is really no choice but to give decltype that reference-adding semantics. The following example, which was provided by Stephan Lavavej, demonstrates that:
template <typename T>
auto array_access(T& array, size_t pos) -> decltype(array[pos]) {
  return array[pos];
}

std::vector<int> vect = {42, 43, 44};
int* p = &vect[0];

array_access(vect, 2) = 45;
array_access(p, 2) = 46;
The last line, where the pointer p is used to access the element, could not be made to work without having decltype add a reference: the type of p[2] is int, any way you turn it. There is no reference in sight here. But p[2] is an lvalue, and by letting decltype add references to lvalues, we can get the desired effect. All this comes with a caveat: the reference that decltype adds is not always what you want. It happens frequently that you need to remove it with remove_reference. We saw an example of that in Section 8.

Here are links to some more articles on auto and decltype that you may find helpful:
  1. Wikipedia article "decltype"
  2. Using C++11 auto and decltype - Code Synthesis
  3. Koenig, Andrew. "A Note About decltype". Dr. Dobb's Journal, July 27, 2011.
  4. Visual C++ Team Blog entry "decltype"