1. decltype keyword
decltype is called a type specifier. Its function is to select and return the data type of the operand.
for example
The return value of the Test2 function is std::initializer_list type
std::initializer_list<int> Test2() { return { 1,2,3 }; }
We use the consistent initial list
decltype(Test2()) Arr1 = { 1,2,3 };
Can run, so the decltype keyword gets the type of the return value of Test2
2. More general examples
const int i = 0; // decltype(i) is const int bool f(const Widget& w); // decltype(w) is const Widget& // decltype(f) is bool(const Widget&) struct Point { int x, y; // decltype(Point::x) is int }; // decltype(Point::y) is int Widget w; // decltype(w) is Widget if (f(w)) ... // decltype(f(w)) is bool template<typename T> // simplified version of std::vector class vector { public: ... T& operator[](std::size_t index); ... }; vector<int> v; // decltype(v) is vector<int> ... if (v[0] == 0) ... // decltype(v[0]) is int&
3.C11
In C++11, the main purpose of this keyword is that you declare a function template whose return value depends on the formal parameter type.
template<typename Container, typename Index> // works, but auto authAndAccess(Container& c, Index i) // requires -> decltype(c[i]) // refinement { return c[i]; }
be careful
① auto in the above code does not represent automatic type derivation, it only represents a syntax
Indicates that the syntax attribute of C++11 tail return type is being used
② The advantage of tail return type is that the parameters of the function can be used to specify the return type
4. C14
C++11 allows type derivation of lambda expressions of monolingual sentences. In C++14, it is extended to all Lambdas and all functions, including multiple statements (or even multiple returns, providing the same derivation type for all outputs). This means that in C++14, we can ignore the tail return type and leave only auto
template<typename Container, typename Index> // C++14; auto authAndAccess(Container& c, Index i) // not quite { // correct return c[i]; // return type deduced from c[i] }
① Using this form, auto does have type derivation
② This means that the compiler will infer the return type of the function from the implementation of the function
The problem is that the compiler infers from auto
According to the previous terms, what will be inferred?
Only its value will be returned, that is, it is an R-value
std::deque<int> d; ... authAndAccess(d, 5) = 10; // authenticate user, return d[5], // then assign 10 to it; // this won't compile!
So how to change this?
template<typename Container, typename Index> // C++14; works, decltype(auto) // but still authAndAccess(Container& c, Index i) // requires { // refinement authenticateUser(); return c[i]; }
5. decltype(auto)
decltype(auto) is not limited to the return value of a function, but can also be used to declare variables
Widget w; const Widget& cw = w;
auto myWidget1 = cw; // auto type deduction: // myWidget1's type is Widget
decltype(auto) myWidget2 = cw; // decltype type deduction: // myWidget2's type is // const Widget&
6. Reanalysis
authAndAccess(Container& c, Index i)
The customer may just want to get a copy
std::deque<std::string> makeStringDeque(); // factory function // make copy of 5th element of deque returned // from makeStringDeque auto s = authAndAccess(makeStringDeque(), 5);
The template cannot pass in an R-value for c, because c is a reference, so the above template still needs to be improved
template<typename Container, typename Index> // c is now a decltype(auto) authAndAccess(Container&& c, // universal Index i); // reference
Now that c is a general reference, you can pass in an R-value or an l-value for c
c is now a generic reference. In this template, we do not know the type of container being operated on, which means that we are also ignorant of the type of index object it uses. In order to make it meet various situations, we need to modify the code again:
template<typename Container, typename Index> // final decltype(auto) // C++14 authAndAccess(Container&& c, Index i) // version { authenticateUser(); return std::forward<Container>(c)[i]; }
C++11 can be written like this
template<typename Container, typename Index> // final auto // C++11 authAndAccess(Container&& c, Index i) // version -> decltype(std::forward<Container>(c)[i]) { authenticateUser(); return std::forward<Container>(c)[i]; }
7.decltype(x) and decltype((x))
The lvalue is derived as a reference by decltype
int x = 0; (x);
(x) Will be treated as an lvalue
decltype(auto) f1() { int x = 0; ... return x; // decltype(x) is int, so f1 returns int } decltype(auto) f2() { int x = 0; ... return (x); // decltype((x)) is int&, so f2 returns int& }
f2 returns a reference to a local variable, which is a disaster for C-series programmers, QAQ
8. Attention
Second point
int nNum1 = 10; decltype(auto)d = nNum1 = 20;
Lvalue expression, so d is derived as int&
nNum1 is not an expression, but a variable name
decltype(auto)d = nNum1;
d is int