<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                條款二:理解`auto`類型推導 ========================= 如果你已經閱讀了條款1關于模板相關的類型推導,你就已經知道了機會所有關于`auto`的類型推導,因為除了一個例外,`auto`類型推導就是模板類型推導。但是它怎么就會是模板類型推導呢?模板類型推導涉及模板和函數以及參數,但是`auto`和上面的這些沒有任何的關系。 這是對的,但是沒有關系。模板類型推導和`auto`類型推導是有一個直接的映射。有一個書面上的從一種情況轉換成另外一種情況的算法。 在條款1,模板類型推導是使用下面的通用模板函數來解釋的: ```cpp template<typename T> void f(ParamType param); ``` 在這里通常調用: ```cpp f(expr); // 使用一些表達式來當做調用f的參數 ``` 在調用`f`的地方,編譯器使用`expr`來推導`T`和`ParamType`的類型。 當一個變量被聲明為`auto`,`auto`相當于模板中的`T`,而對變量做的相關的類型限定就像`ParamType`。這用代碼說明比直接解釋更加容易理解,所以看下面的這個例子: ```cpp auto x = 27; ``` 這里,對`x`的類型定義就僅僅是`auto`本身。從另一方面,在這個聲明中: ```cpp const auto cx = x; ``` 類型被聲明成`const auto`,在這兒: ```cpp const auto& rx = x; ``` 類型被聲明稱`const auto&`。在這些例子中推導`x`,`cx`,`rx`的類型的時候,編譯器處理每個聲明的時候就和處理對應的表達式初始化的模板: ```cpp template<typename T> // 推導x的類型的 void func_for_x(T param); // 概念上的模板 func_for_x(27); // 概念上的調用: // param的類型就是x的類型 template<typename T> void func_for_cx(const T param); // 推導cx的概念上的模板 func_for_cx(x); // 概念調用:param的推導類型就是cx的類型 template<typename T> void func_for_rx(const T& param); // 推導rx概念上的模板 func_for_rx(x); // 概念調用:param的推導類型就是rx的類型 ``` 正如我所說,對`auto`的類型推導只存在一種情況的例外(這個后面就會討論),其他的就和模板類型推導完全一樣了。 條款1把模板類型推導劃分成三部分,基于在通用的函數模板的`ParamType`的特性和`param`的類型聲明。在一個用`auto`聲明的變量上,類型聲明代替了`ParamType`的作用,所以也有三種情況: * 情況1:類型聲明是一個指針或者是一個引用,但不是一個通用的引用 * 情況2:類型聲明是一個通用引用 * 情況3:類型聲明既不是一個指針也不是一個引用 我們已經看了情況1和情況3的例子: ```cpp auto x = 27; // 情況3(x既不是指針也不是引用) const auto cx = x; // 情況3(cx二者都不是) const auto& rx = x; // 情況1(rx是一個非通用的引用) ``` 情況2正如你期待的那樣: ```cpp auto&& uref1 = x; // x是int并且是左值 // 所以uref1的類型是int& auto&& uref2 = cx; // cx是int并且是左值 // 所以uref2的類型是const int& auto&& uref3 = 27; // 27是int并且是右值 // 所以uref3的類型是int&& ``` 條款1講解了在非引用類型聲明里,數組和函數名稱如何退化成指針。這在`auto`類型推導上面也是一樣: ```cpp const char name[] = // name的類型是const char[13] "R. N. Briggs"; auto arr1 = name; // arr1的類型是const char* auto& arr2 = name; // arr2的類型是const char (&)[13] void someFunc(int, double); // someFunc是一個函數,類型是 // void (*)(int, double) auto& func2 = someFunc; // func1的類型是 // void (&)(int, double) ``` 正如你所見,`auto`類型推導和模板類型推導工作很類似。它們就像一枚硬幣的兩面。 除了有一種情況是不一樣的。我們從如果你想聲明一個用27初始化的`int`, C++98你有兩種語法選擇: ```cpp int x1 = 27; int x2(27); ``` C++11,通過標準支持的統一初始化(使用花括號初始化——譯者注),可以添加下面的代碼: ```cpp int x3 = { 27 }; int x4{ 27 }; ``` 綜上四種語法,都會生成一種結果:一個擁有27數值的`int`。 但是正如條款5所解釋的,使用`auto`來聲明變量比使用固定的類型更好,所以在上述的聲明中把`int`換成`auto`更好。最直白的寫法就如下面的代碼: ```cpp auto x1 = 27; auto x2(27); auto x3 = {27}; auto x4{ 27 }; ``` 上面的所有聲明都可以編譯,但是他們和被替換的相對應的語句的意義并不一樣。頭兩個的確是一樣的,聲明一個初始化值為27的`int`。然而后面兩個,聲明了一個類型為`std::intializer_list<int>`的變量,這個變量包含了一個單一的元素27! ```cpp auto x1 = 27; // 類型時int,值是27 auto x2(27); // 同上 auto x3 = { 27 }; // 類型是std::intializer_list<int> // 值是{ 27 } auto x4{ 27 }; // 同上 ``` 這和`auto`的一種特殊類型推導有關系。當使用一對花括號來初始化一個`auto`類型的變量的時候,推導的類型是`std::intializer_list`。如果這種類型無法被推導(比如在花括號中的變量擁有不同的類型),代碼會編譯錯誤。 ```cpp auto x5 = { 1, 2, 3.0 }; // 錯誤! 不能講T推導成 // std::intializer_list<T> ``` 正如注釋中所說的,在這種情況,類型推導會失敗,但是認識到這里實際上是有兩種類型推導是非常重要的。一種是`auto: x5`的類型被推導。因為`x5`的初始化是在花括號里面,`x5`必須被推導成`std::intializer_list`。但是`std::intializer_list`是一個模板。實例是對一些`T`實例化成`std::intializer_list<T>`,這就意味著`T`的類型必須被推導出來。類型推導就在第二種的推導的范圍上失敗了。在這個例子中,類型推導失敗是因為在花括號里面的數值并不是單一類型的。 對待花括號初始化的行為是`auto`唯一和模板類型推導不一樣的地方。當`auto`聲明變量被使用一對花括號初始化,推導的類型是`std::intializer_list`的一個實例。但是如果相同的初始化遞給相同的模板,類型推導會失敗,代碼不能編譯。 ```cpp auto x = { 11, 23, 9 }; // x的類型是 // std::initializer_list<int> template<typename T> // 和x的聲明等價的 void f(T param); // 模板 f({ 11, 23, 9 }); // 錯誤的!沒辦法推導T的類型 ``` 但是,如果你明確模板的`param`的類型是一個不知道`T`類型的`std::initializer_list<T>`: ```cpp template<typename T> void f(std::initializer_list<T> initList); f({ 11, 23, 9 }); // T被推導成int,initList的 // 類型是std::initializer_list<int> ``` 所以`auto`和模板類型推導的本質區別就是`auto`假設花括號初始化代表的是std::initializer_list,但是模板類型推導卻不是。 你可能對為什么`auto`類型推導有一個對花括號初始化有一個特殊的規則而模板的類型推導卻沒有感興趣。我自己也非常奇怪。可是我一直沒有能夠找到一個有力的解釋。但是法則就是法則,這就意味著你必須記住如果使用`auto`聲明一個變量并且使用花括號來初始化它,類型推導的就是`std::initializer_list`。你必須習慣這種花括號的初始化哲學——使用花括號里面的數值來初始化是理所當然的。在C++11編程里面的一個經典的錯誤就是誤被聲明成`std::initializer_list`,而其實你是想聲明另外的一種類型。這個陷阱使得一些開發者僅僅在必要的時候才會在初始化數值周圍加上花括號。(什么時候是必要的會在條款7里面討論。) 對于C++11,這是一個完整的故事,但是對于C++14來說,故事還要繼續。C++14允許`auto`表示推導的函數返回值(參看條款3),而且C++14的lambda可能會在參數聲明里面使用`auto`。但是,這里面的使用是復用了模板的類型推導,而不是`auto`的類型推導。所以一個使用`auto`聲明的返回值的函數,返回一個花括號初始化就無法編譯。 ```cpp auto createInitList() { return { 1, 2, 3 }; // 編譯錯誤:不能推導出{ 1, 2, 3 }的類型 } ``` 在C++14的lambda里面,當`auto`用在參數類型聲明的時候也是如此: ```cpp std::vector<int> v; … auto resetV = [&v](const auto& newValue) { v = newValue; } // C++14 … resetV({ 1, 2, 3 }); // 編譯錯誤,不能推導出{ 1, 2, 3 }的類型 ``` |要記住的東西| | :--------- | |`auto`類型推導通常和模板類型推導類似,但是`auto`類型推導假定花括號初始化代表的類型是`std::initializer_list`,但是模板類型推導卻不是這樣| |`auto`在函數返回值或者lambda參數里面執行模板的類型推導,而不是通常意義的`auto`類型推導|
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看