1. 程式人生 > >c++11-17 模板核心知識(九)—— 理解decltype與decltype(auto)

c++11-17 模板核心知識(九)—— 理解decltype與decltype(auto)

- [decltype介紹](#decltype介紹) - [為什麼需要decltype](#為什麼需要decltype) - [decltype(auto)](#decltypeauto) - [注意(entity)](#注意entity) 與模板引數推導和auto推導一樣,decltype的結果大多數情況下是正常的,但是也有少部分情況是反直覺的。 ## decltype介紹 給定一個name或者expression,decltype會告訴你它的型別。 我們先從正常情況開始: ```c++ 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 // simplified version of std::vector class vector { public: … T& operator[](std::size_t index); … }; vector v; // decltype(v) is vector … if (v[0] == 0) … // decltype(v[0]) is int& ``` 很直觀,沒有例外情況。 **注意:decltype與auto不同,不會消除const和引用。** ## 為什麼需要decltype 比如我們需要宣告一個函式模板,函式的返回值型別依賴函式引數的型別。在C++11中,常見的例子是返回一個container對應索引的值: ```c++ template // works, but requires refinement auto authAndAccess(Container &c, Index i) -> decltype(c[i]) { return c[i]; } ``` 注意:這裡的auto跟型別推導沒有任何關係,它只是表明了這裡使用了C++11的`trailing return type`. ### decltype(auto) 在C++11中只允許單語句的lambda表示式被推導,在C++14中之中行為被拓展到所有lambda和所有函式,包括多語句。在C++14中,上述程式碼我們可以簡寫為: ```c++ template // C++14; not quite correct auto authAndAccess(Container& c, Index i) { return c[i]; // return type deduced from c[i] } ``` **注意:這裡的auto就跟型別推導有關係了。** 在前面講auto推導規則的文章中提到過,auto作用在函式返回值時,使用的是模板引數推導規則,**這裡就會出現問題:`operator []`我們希望它返回引用,但是使用auto使用模板引數推導規則時,引用會被忽略,所以下面的程式碼會報錯:** ```c++ template auto authAndAccess(Container &c, Index i) { return c[i]; } std::vector v{1,2,3,4,5}; authAndAccess(v,2) = 10; // error: expression is not assignable ``` 但是使用`auto -> decltype()`則不會報錯,因為這裡auto不代表引數引數推導: ```c++ template auto authAndAccess(Container &c, Index i) -> decltype(c[i]) { return c[i]; } std::vector v{1,2,3,4,5}; authAndAccess(v,2) = 10; ``` 所以,要想讓authAndAccess在使用auto的情況下返回引用,在C++14中,我們可以使用decltype(auto): ```c++ template decltype(auto) authAndAccess(Container &c, Index i) { return c[i]; } std::vector v{1,2,3,4,5}; authAndAccess(v,2) = 10; ``` decltype(auto)中的auto代表返回值需要被自動推導,decltype代表使用decltype來推導返回值型別。 decltype(auto)不僅可以宣告函式返回值,還可以宣告變數: ```c++ Widget w; const Widget& cw = w; // auto type deduction : myWidget1's type is Widget decltype(auto) myWidget2 = cw; // decltype type deduction : myWidget2's type is const Widget& ``` ## 注意(entity) decltype的規則可以看官網:[decltype specifier](https://en.cppreference.com/w/cpp/language/decltype),概況下可以分為兩大類: * `decltype ( entity )` : 如果entity是一個不被括號包圍的識別符號、類訪問表示式,那麼`decltype ( entity )`與entity型別一致。 * `decltype ( expression )` : 如果expression是一個表示式,計算結果為型別T,那麼: * 如果expression為xvalue,那麼decltype的結果是T&&. * 如果expression為lvalue,那麼decltype的結果是T&. * 如果expression為prvalue,那麼decltype的結果是T. 注意第一點中強調了entity是一個**不被括號包圍的**識別符號。因為當一個識別符號被括號包圍時,它就是一個左值表示式了,對應上面第二大點的第二小點。比如說`int x = 0;`,x是一個識別符號,所以`decltype(x)`的結果為int。但是(x)就是一個左值表示式,`decltype((x))`的結果就是int&。所以下面的用法是不同的: 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& } 官網的例子能很好的概況decltype最常見的用法: ```c++ #include struct A { double x; }; const A* a; decltype(a->x) y; // type of y is double (declared type) decltype((a->x)) z = y; // type of z is const double& (lvalue expression) template auto add(T t, U u) -> decltype(t + u) // return type depends on template parameters // return type can be deduced since C++14 { return t + u; } int main() { int i = 33; decltype(i) j = i * 2; std::cout << "i = " << i << ", " << "j = " << j << '\n'; auto f = [](int a, int b) -> int { return a * b; }; decltype(f) g = f; // the type of a lambda function is unique and unnamed i = f(2, 2); j = g(3, 3); std::cout << "i = " << i << ", " << "j = " << j << '\n'; } ``` (完) 朋友們可以關注下我的公眾號,獲得最及時的更新: ![](https://img2020.cnblogs.com/blog/1298604/202011/1298604-20201103132150036-885052