<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之旅 廣告
                條款三:理解`decltype` ========================= `decltype`是一個怪異的發明。給定一個變量名或者表達式,`decltype`會告訴你這個變量名或表達式的類型。`decltype`的返回的類型往往也是你期望的。然而有時候,它提供的結果會使開發者極度抓狂而不得參考其他文獻或者在線的Q&A網站。 我們從在典型的情況開始討論,這種情況下`decltype`不會有令人驚訝的行為。與`templates`和`auto`在類型推導中行為相比(請見條款一和條款二),`decltype`一般只是復述一遍你所給他的變量名或者表達式的類型,如下: ```cpp 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 }; 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& ``` 看到沒有?毫無令人驚訝的地方。 在C++11中,`decltype`最主要的用處可能就是用來聲明一個函數模板,在這個函數模板中返回值的類型取決于參數的類型。舉個例子,假設我們想寫一個函數,這個函數中接受一個支持方括號索引(也就是"[]")的容器作為參數,驗證用戶的合法性后返回索引結果。這個函數的返回值類型應該和索引操作的返回值類型是一樣的。 操作子`[]`作用在一個對象類型為`T`的容器上得到的返回值類型為`T&`。對`std::deque`一般是成立的,例如,對`std::vector`,這個幾乎是處處成立的。然而,對`std::vector<bool>`,`[]`操作子不是返回`bool&`,而是返回一個全新的對象。發生這種情況的原理將在條款六中討論,對于此處重要的是容器的`[]`操作返回的類型是取決于容器的。 `decltype`使得這種情況很容易來表達。下面是一個模板程序的部分,展示了如何使用`decltype`來求返回值類型。這個模板需要改進一下,但是我們先推遲一下: ```cpp template<typename Container, typename Index> // works, but auto authAndAccess(Container& c, Index i) // requires -> decltype(c[i]) // refinements { authenticateUser(); return c[i]; } ``` 將`auto`用在函數名之前和類型推導是沒有關系的。更精確地講,此處使用了`C++11`的尾隨返回類型技術,即函數的返回值類型在函數參數之后聲明(“->”后邊)。尾隨返回類型的一個優勢是在定義返回值類型的時候使用函數參數。例如在函數`authAndAccess`中,我們使用了`c`和`i`定義返回值類型。在傳統的方式下,我們在函數名前面聲明返回值類型,`c`和`i`是得不到的,因為此時`c`和`i`還沒被聲明。 使用這種類型的聲明,`authAndAccess`的返回值就是`[]`操作子的返回值,這正是我們所期望的。 `C++11`允許單語句的`lambda`表達式的返回類型被推導,在`C++14`中之中行為被拓展到包括多語句的所有的`lambda·表達式和函數。在上面`authAndAccess`中,意味著在`C++14`中我們可以忽略尾隨返回類型,僅僅保留開頭的`auto`。使用這種形式的聲明, 意味著將會使用類型推導。特別注意的是,編譯器將從函數的實現來推導這個函數的返回類型: ```cpp template<typename Container, typename Index> // C++14; auto authAndAccess(Container &c, Index i) // not quite { // correct authenticateUser(); return c[i]; } // return type deduced from c[i] ``` <font color='#990000'>條款二</font>解釋說,對使用`auto`來表明函數返回類型的情況,編譯器使用模板類型推導。但是這樣是回產生問題的。正如我們所討論的,對絕大部分對象類型為`T`的容器,`[]`操作子返回的類型是`&T`, 然而<font color='#990000'>條款一</font>提到,在模板類型推導的過程中,初始表達式的引用會被忽略。思考這對下面代碼意味著什么: ```cpp std::deque<int> d; ... authAndAccess(d, 5) = 10; // authenticate user, return d[5], // then assign 10 to it; // this won't compile! ``` 此處,`d[5]`返回的是`int&`,但是`authAndAccess`的`auto`返回類型聲明將會剝離這個引用,從而得到的返回類型是`int`。`int`作為一個右值成為真正的函數返回類型。上面的代碼嘗試給一個右值`int`賦值為10。這種行為是在`C++`中被禁止的,所以代碼無法編譯通過。 為了讓`authAndAccess`按照我們的預期工作,我們需要為它的返回值使用`decltype`類型推導,即指定`authAndAccess`要返回的類型正是表達式`c[i]`的返回類型。`C++`的擁護者們預期到在某種情況下有使用`decltype`類型推導規則的需求,并將這個功能在`C++14`中通過`decltype(auto)`實現。這使這對原本的冤家(`decltype`和`auto`)在一起完美地發揮作用:`auto`指定需要推導的類型,`decltype`表明在推導的過程中使用`decltype`推導規則。因此,我們可以重寫`authAndAccess`如下: ```cpp template<typename Container, typename Index> // C++14; works, decltype(auto) // but still authAndAccess(Container &c, Index i) // requires { // refinement authenticateUser(); return c[i]; } ``` 現在`authAndAccess`的返回類型就是`c[i]`的返回類型。在一般情況下,`c[i]`返回`T&`,`authAndAccess`就返回`T&`,在不常見的情況下,`c[i]`返回一個對象,`authAndAccess`也返回一個對象。 `decltype(auto)`并不僅限使用在函數返回值類型上。當時想對一個表達式使用`decltype`的推導規則時,它也可以很方便的來聲明一個變量: ```cpp 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& ``` 我知道,到目前為止會有兩個問題困擾著你。一個是我們前面提到的,對`authAndAccess`的改進。我們在這里討論。 再次看一下`C++14`版本的`authAndAccess`的聲明: ```cpp template<typename Container, typename Index> decltype(auto) anthAndAccess(Container &c, Index i); ``` 這個容器是通過非`const`左值引用傳入的,因為通過返回一個容器元素的引用是來修改容器是被允許的。但是這也意味著不可能將右值傳入這個函數。右值不能和一個左值引用綁定(除非是`const`的左值引用,這不是這里的情況)。 誠然,傳遞一個右值容器給`authAndAccess`是一種極端情況。一個右值容器作為一個臨時對象,在 `anthAndAccess` 所在語句的最后被銷毀,意味著對容器中一個元素的引用(這個引用通常是`authAndAccess`返回的)在創建它的語句結束的地方將被懸空。然而,這對于傳給`authAndAccess`一個臨時對象是有意義的。一個用戶可能僅僅想拷貝一個臨時容器中的一個元素,例如: ```cpp std::deque<std::string> makeStringDeque(); // factory function // make copy of 5th element of deque returned // from makeStringDeque auto s = authAndAccess(makeStringDeque(), 5); ``` 支持這樣的應用意味著我們需要修改`authAndAccess`的聲明來可以接受左值和右值。重載可以解決這個問題(一個重載負責左值引用參數,另外一個負責右值引用參數),但是我們將有兩個函數需要維護。避免這種情況的一個方法是使`authAndAccess`有一個既可以綁定左值又可以綁定右值的引用參數,條款24將說明這正是統一引用(`universal reference`)所做的。因此`authAndAccess`可以像如下聲明: ```cpp template<typename Container, typename Index> // c is now a decltype(auto) authAndAccess(Container&& c, // universal Index i); // reference ``` 在這個模板中,我們不知道我們在操作什么類型的容器,這也意味著我們等同地忽略了它用到的索引對象的類型。對于一個不清楚其類型的對象使用傳值傳遞通常會冒一些風險,比如因為不必要的復制而造成的性能降低,對象切片的行為問題,被同事嘲笑,但是對容器索引的情況,正如一些標準庫的索引(`std::string, std::vector, std::deque`的`[]`操作)按值傳遞看上去是合理的,因此對它們我們仍堅持按值傳遞。 然而,我們需要更新這個模板的實現,將`std::forward`應用給統一引用,使得它和條款25中的建議是一致的。 ```cpp template<typename Container, typename Index> // final decltype(auto) // C++14 authAndAccess(Container&& c, Index i) // version { authenticateUser(); return std::forward<Container>(c)[i]; } ``` 這個實現可以做我們期望的任何事情,但是它要求使用支持`C++14`的編譯器。如果你沒有一個這樣的編譯器,你可以使用這個模板的`C++11`版本。它出了要你自己必須指定返回類型以外,和對應的`C++14`版本是完全一樣的, ```cpp 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]; } ``` 另外一個容易被你挑刺的地方是我在本條款開頭的那句話:`decltype`幾乎所有時候都會輸出你所期望的類型,但是有時候它的輸出也會令你吃驚。誠實的講,你不太可能遇到這種以外,除非你是一個重型庫的實現人員。 為了徹底的理解`decltype`的行為,你必須使你自己對一些特殊情況比較熟悉。這些特殊情況太晦澀難懂,以至于很少有書會像本書一樣討論,但是同時也可以增加我們對`decltype`的認識。 對一個變量名使用`decltype`得到這個變量名的聲明類型。變量名屬于左值表達式,但這并不影響`decltype`的行為。然而,對于一個比變量名更復雜的左值表達式,`decltype`保證返回的類型是左值引用。因此說,如果一個非變量名的類型為`T`的左值表達式,`decltype`報告的類型是`T&`。這很少產生什么影響,因為絕大部分左值表達式的類型有內在的左值引用修飾符。例如,需要返回左值的函數返回的總是左值引用。 這種行為的意義是值得我們注意的。但是在下面這個語句中 ```cpp int x = 0; ``` `x`是一個變量名,因此`decltyper(x)`是`int`。但是如果給`x`加上括號"(x)"就得到一個比變量名復雜的表達式。作為變量名,`x`是一個左值,同時`C++`定義表達式`(x)`也是左值。因此`decltype((x))`是`int&`。給一個變量名加上括號會改變`decltype`返回的類型。 在`C++11`中,這僅僅是個好奇的探索,但是和`C==14`中對`decltype(auto)`支持相結合,函數中返回語句的一個細小改變會影響對這個函數的推導類型。 ```cpp 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 return int& } ``` `f2`不僅返回值類型與`f1`不同,它返回的是對一個局部變量的引用。這種類型的代碼將把你帶上一個為定義行為的快速列車-你完全不想登上的列車。 最主要的經驗教訓就是當使用`decltype(auto)`時要多留心一些。被推導的表達式中看上去無關緊要的細節都可能影響`decltype`返回的類型。為了保證推導出的類型是你所期望的,請使用條款4中的技術。 同時不能更大視角上的認識。當然,`decltype`(無論只有`decltype`或者還是和`auto`聯合使用)有可能偶爾會產生類型推導的驚奇行為,但是這不是常見的情況。一般情況下,`decltype`會產生你期望的類型。將`decltype`應用于變量名無非是正確的,因為在這種情況下,`decltype`做的就是報告這個變量名的聲明類型。 |要記住的東西| |:--------- | |`decltype`幾乎總是得到一個變量或表達式的類型而不需要任何修改| |對于非變量名的類型為`T`的左值表達式,`decltype`總是返回`T&`| |`C++14`支持`decltype(auto)`,它的行為就像`auto`,從初始化操作來推導類型,但是它推導類型時使用`decltype`的規則|
                  <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>

                              哎呀哎呀视频在线观看