A Note About decltype
decltype for space reasons: decltype is syntactically overloaded with two different, but related, meanings.
Last week's article about C++0x left out a subtlety about decltype for space reasons: decltype is syntactically overloaded with two different, but related, meanings.
If n is a name (such as a variable or a class member), decltype(n) is the type that n is declared to have. If the declaration has a reference type, decltype(n) also has a reference type. For example:
int i; // decltype(i) is int int& j = i; // decltype(j) is int&
If expr represents an expression that is not merely a name, then decltype(expr) is the type that evaluating expr would yield. I say would here because expr is not actually evaluated. Moreover, the result of decltype(expr) is a reference only if the expression yields an lvalue.
For example, after the definitions of i and j above, decltype(j+1) is int, decltype(i+1) is also int, and decltype((i)) is int&. It may be surprising that decltype(i) and decltype((i)) are two different types, but this difference comes about because i is a variable name and (i) is not.
One typically applies decltype to a variable as part of defining the type of a variable to be "just like that one over there." Such declarations are particularly useful when you combine them with auto. For example, after we have written
auto var = <i>expression</i>;
we may want to create another variable with the same type as var. We can do this by writing
auto var2 = var;
However, we might not want to initialize var2 at this point, perhaps because we are about to read into it:
cin >> var2;
We can define var2 to have the appropriate type without having to initialize it:
decltype(var) var2;
Programmers often apply decltype to expressions in order to write code that depends on those expressions' types. For example, the type of an ordinary integer literal is int if int is capable of containing the literal. Otherwise, it is long (and perhaps even long long). Therefore, decltype(1000000) is an easy way of writing "the narrowest integer type that is capable of containing the value 1000000."
As a more complicated example, consider a container c with an element type that we don't know. We would like to apply a function f to some of that container's elements and put f's results into a vector. How do we define that vector?
We can almost — but not quite — get away with writing
// <i>This definition fails if</i> f <i>returns a reference</i> vector<decltype(f(c[0]))> v;
This definition has the right idea: It uses decltype to find out what type would result from applying f to an element of c. This application is always safe, even if c[0] does not exist, because the expression given to decltype is never actually evaluated; the compiler just figures out what type it would have if it were evaluated.
The trouble is that when we give an expression to decltype, decltype yields a reference type if the expression yields an lvalue. Without inspecting the definition of f, we don't know whether f returns an lvalue.
To make it easier to write programs such as this one, C++0x defines a library template named remove_reference (and several others with names such as add_reference, remove_const, and so on). We use this template this way:
// <i>This definition works even if</i> f <i>returns a reference</i> vector<remove_reference<decltype(f(c[0]))>::type> v;
The general idea is that remove_reference is a template type that contains a type member named type. This member is the name of a type that matches remove_reference's template parameter, but with any top-level & removed. So, for example, remove_reference<int&>::type is a roundabout way of writing int.
This article just scratches the surface of the kinds of type manipulations that decltype makes possible. Fortunately, one does not need to understand them all in order to use decltype. The main point to remember is that decltype is really a single name for two related — but subtly different — concepts. One of them is determining the type of the declaration associated with a variable; the other is determining the type that would result from evaluating an expression. The rules for the two forms of decltype, especially when references are involved, are slightly different; it is important to understand those differences if you write programs that might depend on them.

