Is
auto x = initializer;
equivalent to
decltype(initializer) x = initializer;
or
decltype
autoauto is simple: it will give the same type as by-value template parameter deduction. auto works uniformly on expressions.
template
void deduce(T x);
int &refint();
std::string str();
std::string const conststr();
auto i1 = 1; // deduce(1) gives T=int so int i1
auto i2 = i1; // deduce(i1) gives T=int so int i2
auto i3 = refint(); // deduce(refint()) gives T=int so int i3
const auto ci1 = i1; // deduce(i1) gives T=int so const int ci1
auto i4 = ci1; // deduce(ci1) gives T=int so int i4
auto s1 = std::string(); // std::string s1
auto s2 = str(); // std::string s2
auto s3 = conststr(); // std::string s3
In C++ expressions cannot have reference type (refint() has type int not int&).
Note that the lvalueness of the expression is not an issue on an expression on the right (on the right of equal sign, or something which is being copied in general). The rvalue 1 is treated like the lvalues i1 and refint().
For by value parameters (that is, non reference parameters), not only lvalue to rvalue conversion is applied, but also array to pointer conversions. const is ignored.
decltypedecltype is a very useful feature with an horrible interface:
decltype acts differently on some expressions defined in term of name lookup and other expressions! This is the type of features that make people hate C++.
decltype of something nameddecltype(entity) will do name lookup and give the declared type of the entity. (entity can be an unqualified or qualified identifier, or a member access such as expr.identifier.)
decltype(f(args)) will do name lookup and overload resolution and give the declared return type of the function, not the type the expression:
extern decltype(refint()) ri1; // int &ri1
So now I can check my understanding of the language with decltype:
template
struct sametype {};
template
struct sametype {typedef int same;};
sametype exists iff T and U are exactly the same type.
sametype::same check_i1;
sametype::same check_i2;
sametype::same check_i3;
sametype::same check_i4;
sametype::same check_ci1;
sametype::same check_ir1;
sametype::same check_s1;
sametype::same check_s2;
sametype::same check_s3;
compiles fine, so I was not wrong!
decltype of other expressionsOtherwise, where expr is not defined in term of name lookup (not one of the above cases), such as the expression (expr), decltype implements a distinct feature (but the C++ designers would not spent another keywords on it.)
decltype(expr) will give the type of the expression decorated with its lxrvalueness (lvalue/xvalue/prvalue-ness):
T gives TT gives T&&T gives T&This is the reciprocal of function call rule: if f is a function with return type
T&, the expression f() is an lvalueT&&, the expression f() is an xvalueT, the expression f() is a prvalueand also for casts: for naked type (pure object type, non reference) T
(T&)expr is a lvalue(T&&)expr is an xvalue(T)expr is a prvalueReference-ness is the encoding of lxrvalueness into types. decltype does this encoding to save and transfer the lxrvalueness of things.
This is useful when you want to alias an expression: the lxrvalueness of an expression expr that is a function call (either normal f(args) or with operator syntax like a @ b) is the same as the lxrvalueness of alias() declared as decltype(expr) alias();
This can be used for pure forwarding in generic code:
// decorated type of an expression
#define EXPR_DEC_TYPE(expr) decltype((expr))
int i;
int &ri = i;
int fi();
int &fri();
EXPR_DEC_TYPE(i) alias_i = i; // int &
EXPR_DEC_TYPE(ri) alias_ri = ri; // int &
EXPR_DEC_TYPE(fi()) alias_fi(); // int alias_fi()
EXPR_DEC_TYPE(fri()) alias_fri(); // int &alias_fri()
Note that EXPR_DEC_TYPE(foo()) equals declexpr(foo()) by design (in most cases), but the computations are different:
declexpr(foo(args)) does name lookup for foo, does overload
resolution, finds the declaration, returns the exact return type
declared, end of story
EXPR_DEC_TYPE(foo(args)) finds the type of the declaration then
computes
the type T of the expression which is the return type naked (without
reference)
the lxrvalueness LXR of the expression according to the referenceness of the declared return type: lvalue for reference, xvalue of r-reference...
then it decorates the type T with LXR to obtain the decT type:
decT is T if LXR = prvaluedecT is T&& if LXR = xvaluedecT is T& if LXR = lvalueEXPR_DEC_TYPE returns decT which is the same as the declared return type.