Feb 9, 2012

[c++11][Note] The relationship between auto and decltype



http://stackoverflow.com/questions/6869888/the-relationship-between-auto-and-decltype


decltype also considers whether the expression is rvalue or lvalue .


Wikipedia says,


The type denoted by decltype can be different from the type deduced by auto.

#include <vector>
int main() { 
    const std::vector <int> v(1); 
    auto a = v[0]; // a has type int 
    decltype(v[0]) b = 1; // b has type const int&, the return type of // std::vector<int>::operator[](size_type) const 
    auto c = 0; // c has type int 
    auto d = c; // d has type int 
    decltype(c) e; // e has type int, the type of the entity named by c 
    decltype((c)) f = c; // f has type int&, because (c) is an lvalue 
    decltype(0) g; // g has type int, because 0 is an rvalue
}



That pretty much explains the imporant difference.
Notice decltype(c) and decltype((c)) are not same!


And sometime auto and decltype works together in a cooperative way, such as in the following example (taken from wiki, and modified a bit):
int& foo(int& i);
float foo(float& f); 
template <<class T> auto f(T& t) −> decltype(foo(t)) {
   return foo(t);
}



Wikipedia further explains the semantics of decltype as follows:


Similarly to the sizeof operator, the operand of decltype is unevaluated.

Informally, the type returned by decltype(e) is deduced as follows:
[reference: http://en.cppreference.com/w/cpp/language/decltype ]

1) If the argument is an unparenthesized id-expression or an unparenthesized class member access, then decltype yields the type of the entity named by this expression. If there is no such entity, or if the argument names a set of overloaded functions, the program is ill-formed.
2) If the argument is any other expression of type T, and
a) if the value category of expression is xvalue, then decltype yields T&&;
b) if the value category of expression is lvalue, then decltype yields T&;
c) if the value category of expression is prvalue, then decltype yields T.

If expression is a function call which returns a prvalue of class type or is a comma expression whose right operand is such a function call, a temporary object is not introduced for that prvalue. The class type need not be complete or have an available destructor. This rule doesn't apply to sub-expressions: in decltype(f(g())), g() must have a complete type, but f() need not.

Note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus decltype(x) and decltype((x)) are often different types.

decltype is useful when declaring types that are difficult or impossible to declare using standard notation, like lambda-related types or types that depend on template parameters.



These semantics were designed to fulfill the needs of generic library writers, while at the same time being intuitive for novice programmers, because the return type of decltype always matches the type of the object or function exactly as declared in the source code.

More formally, Rule 1 applies to unparenthesized id-expressions and class member access expressions.
For function calls, the deduced type is the return type of the statically chosen function, as determined by the rules for overload resolution.
Example:
const int&& foo(); 
int i;
struct A { double x; };
const A* a = new A(); 
decltype(foo()) x1; // type is const int&& 
decltype(i) x2; // type is int
decltype(a->x) x3; // type is double
decltype((a->x)) x4; // type is const double&

The reason for the difference between the latter two invocations of decltype is that the parenthesized expression (a->x) is neither an id-expression nor a member access expression, and therefore does not denote a named object.
Because the expression is an lvalue, its deduced type is "reference to the type of the expression", or const double&.

==========

Further read:

Appearing and Disappearing consts in C++

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.