Is
auto x = initializer;
equivalent to
decltype(initializer) x = initializer;
or
decltype((initializer)) x = initializer;
or neither?
In the C++ programming language, decltype is a keyword used to query the type of an expression. Introduced in C++11, its primary intended use is in generic programming, where it is often difficult, or even impossible, to express types that depend on template parameters.
Decltype keyword in C++ Decltype stands for declared type of an entity or the type of an expression. It lets you extract the type from the variable so decltype is sort of an operator that evaluates the type of passed expression. SYNTAX : decltype( expression )
decltype is a compile time evaluation (like sizeof ), and so can only use the static type.
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:
- If the expression e refers to a variable in local or namespace scope, a static member variable or a function parameter, then the result is that variable's or parameter's declared type
- If e is a function call or an overloaded operator invocation, decltype(e) denotes the declared return type of that function
- Otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e; if e is an rvalue, the result is T
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&.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With