decltype introduction
The emergence of type derivation at compile time is just for generic programming. In non generic programming, our types are determined and there is no need to deduce at all.
For compile time type derivation, in addition to the auto keyword we mentioned, there is also decltype in this paper.
Decltype, like the auto keyword, is used for compile time type derivation, but it is still different from auto. The type derivation of decltype does not obtain the type of variable from the initialization expression declared by the variable like auto, but always takes an ordinary expression as a parameter to return the type of the expression, and decltype does not evaluate the expression.
decltype usage
Derived expression type
int i = 4; decltype(i) a; //The derivation result is int. The type of a is int.
Used with using/typedef to define types.
using size_t = decltype(sizeof(0));//The return value of sizeof(a) is size_t type using ptrdiff_t = decltype((int*)0 - (int*)0); using nullptr_t = decltype(nullptr);
vector<int >vec; typedef decltype(vec.begin()) vectype; for (vectype i = vec.begin; i != vec.end(); i++) { //... }
Like auto, this also improves the readability of the code.
Reuse anonymous types
In C + +, we sometimes encounter some anonymous types, such as:
struct { int d ; doubel b; }anon_s;
With decltype, we can reuse this anonymous structure:
decltype(anon_s) as ;//Defines an anonymous structure above
Generic programming, combined with auto, is used to track the return value type of a function
This is also the biggest use of decltype.
template <typename _Tx, typename _Ty> auto multiply(_Tx x, _Ty y)->decltype(_Tx*_Ty) { return x*y; }
decltype derivation four rules
- If e is a marker expression without parentheses or a class member access expression, the decltype (E) of is the type of the entity named by E. In addition, if e is an overloaded function, it will cause compilation errors.
- Otherwise, assume that the type of E is T. if e is a dead value, decltype (E) is T&&
- Otherwise, assume that the type of E is T. if e is an lvalue, decltype (E) is T&.
- Otherwise, assuming that the type of e is T, decltype (e) is T.
Tags refer to the tags defined by the programmer except for the tags required by the compiler such as keywords and literals, and the expression corresponding to a single tag is the tag expression. For example:
int arr[4]
Then arr is a marker expression, and arr[3]+0 is not.
Let's look at the following code:
int i=10; decltype(i) a; //a is derived as int decltype((i))b=i;//b is derived as int &, which must be initialized, otherwise compilation error
Just adding () to i leads to the difference of type derivation results. This is because i is a marker expression. According to derivation rule 1, the type is deduced as int. And (i) is an lvalue expression, so the type is deduced as int&.
The following code can further understand the derivation of the four rules
int i = 4; int arr[5] = { 0 }; int *ptr = arr; struct S{ double d; }s ; void Overloaded(int); void Overloaded(char);//Overloaded function int && RvalRef(); const bool Func(int); //Rule 1: derivation as its type decltype (arr) var1; //int marker expression decltype (ptr) var2;//int * marker expression decltype(s.d) var3;//Double member access expression //decltype(Overloaded) var4;// Overloaded functions. Compilation error. //Rule 2: lose value. Derived as an R-value reference of type. decltype (RvalRef()) var5 = 1; //Rule 3: lvalues are derived as references to types. decltype ((i))var6 = i; //int& decltype (true ? i : i) var7 = i; //Int & the conditional expression returns an lvalue. decltype (++i) var8 = i; //Int & + i returns the lvalue of i. decltype(arr[5]) var9 = i;//int&. [] operation returns lvalue decltype(*ptr)var10 = i;//Int & * operation returns lvalue decltype("hello")var11 = "hello"; //Const char (&) [9] string literal constant is lvalue and const lvalue. //Rule 4: if none of the above is true, it is deduced as this type decltype(1) var12;//const int decltype(Func(1)) var13=true;//const bool decltype(i++) var14 = i;//int i + + returns the right value
It should be noted here that the string literal constant is an lvalue and const lvalue, while the non string literal constant is an lvalue.
So many rules are hard for us to remember, especially rule 3. We can use the template class is added in the C++11 standard library_ lvalue_ Reference to determine whether the expression is an lvalue:
cout << is_lvalue_reference<decltype(++i)>::value << endl;
The result 1 is expressed as a left value, and the result 0 is a non right value.
Similarly, there is also is_rvalue_reference to determine whether the decltype inference result is an R-value.