<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>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                條款9:優先使用聲明別名而不是`typedef` ========================= 我有信心說,大家都同意使用`STL`容器是個好的想法,并且我希望,條款18可以說服你使用`std::unique_ptr`也是個好想法,但是我想絕對我們中間沒有人喜歡寫像這樣`std::unique_ptr<std::unordered_map<std::string, std::string>>`的代碼多于一次。這僅僅是考慮到這樣的代碼會增加得上“鍵盤手”的風險。 為了避免這樣的醫療悲劇,推薦使用一個`typedef`: ```cpp typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS; ``` 但是`typedef`家族是有如此濃厚的`C++98`氣息。他們的確可以在`C++11`下工作,但是`C++11`也提供了聲明別名(`alias declarations`): ```cpp using UptrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>; ``` 考慮到`typedef`和聲明別名具有完全一樣的意義,推薦其中一個而排斥另外一個的堅實技術原因是容易令人質疑的。這樣的質疑是合理的。 技術原因當然存在,但是在我提到之前。我想說的是,很多人發現使用聲明別名可以使涉及到函數指針的類型的聲明變得容易理解: ```cpp // FP等價于一個函數指針,這個函數的參數是一個int類型和 // std::string常量類型,沒有返回值 typedef void (*FP)(int, const std::string&); // typedef // 同上 using FP = void (*)(int, const std::string&); // 聲明別名 ``` 當然,上面任何形式都不是特別讓人容易下咽,并且很少有人會花費大量的時間在一個函數指針類型的標識符上,所以這很難當做選擇聲明別名而不是`typedef`的不可抗拒的原因。 但是,一個不可抗拒的原因是真實存在的:模板。尤其是聲明別名有可能是模板化的(這種情況下,它們被稱為模板別名(`alias template`)),然而`typedef`這是只能說句“臣妾做不到”。模板別名給`C++11`程序員提供了一個明確的機制來表達在`C++98`中需要黑客式的將`typedef`嵌入在模板化的`struct`中才能完成的東西。舉個栗子,給一個使用個性化的分配器`MyAlloc`的鏈接表定義一個標識符。使用別名模板,這就是小菜一碟: ```cpp template<typname T> // MyAllocList<T> using MyAllocList = std::list<T, MyAlloc<T>>; // 等同于 // std::list<T, // MyAlloc<T>> MyAllocList<Widget> lw; // 終端代碼 ``` 使用`typedef`,你不得不從草稿圖開始去做一個蛋糕: ```cpp template<typename T> // MyAllocList<T>::type struct MyAllocList { // 等同于 typedef std::list<T, MyAlloc<T>> type; // std::list<T, }; // MyAlloc<T>> MyAllocList<Widget>::type lw; // 終端代碼 ``` 如果你想在一個模板中使用`typedef`來完成創建一個節點類型可以被模板參數指定的鏈接表的任務,你必須在`typedef`名稱之前使用`typename`: ```cpp template<typename T> // Widget<T> 包含 class Widget{ // 一個 MyAloocList<T> private: // 作為一個數據成員 typename MyAllocList<T>::type list; ... }; ``` 此處,`MyAllocList<T>::type`表示一個依賴于模板類型參數`T`的類型,因此`MyAllocList<T>::type`是一個依賴類型(`dependent type`),`C++`中許多令人喜愛的原則中的一個就是在依賴類型的名稱之前必須冠以`typename`。 如果`MyAllocList`被定義為一個聲明別名,就不需要使用`typename`(就像笨重的`::type`后綴): ```cpp template<typname T> using MyAllocList = std::list<T, MyAlloc<T>>; // 和以前一樣 template<typename T> class Widget { private: MyAllocList<T> list; // 沒有typename ... // 沒有::type }; ``` 對你來說,`MyAllocList<T>`(使用模板別名)看上去依賴于模板參數`T`,正如`MyAllocList<T>::type`(使用內嵌的`typdef`)一樣,但是你不是編譯器。當編譯器處理`Widget`遇到`MyAllocList<T>`(使用模板別名),編譯器知道`MyAllocList<T>`是一個類型名稱,因為`MyAllocList`是一個模板別名:它必須是一個類型。`MyAllocList<T>`因此是一個非依賴類型(`non-dependent type`),指定符`typename`是不需要和不允許的。 另一方面,當編譯器在`Widget`模板中遇到`MyAllocList<T>`(使用內嵌的`typename`)時,編譯器并不知道它是一個類型名,因為有可能存在一個特殊化的`MyAllocList`,只是編譯器還沒有掃描到,在這個特殊化的`MyAllocList`中`MyAllocList<T>::type`表示的并不是一個類型。這聽上去挺瘋狂的,但是不要因為這種可能性而怪罪于編譯器。是人類有可能會寫出這樣的代碼。 例如,一些被誤導的鬼魂可能會雜糅出像這樣代碼: ```cpp class Wine {...}; template<> // 當T時Wine時 class MyAllocList<Wine>{ // MyAllocList 是特殊化的 private: enum class WineType // 關于枚舉類參考條款10 { White, Red, Rose }; WineType type; // 在這個類中,type是個數據成員 ... }; ``` 正如你看到的,`MyAllocList<Wine>::type`并不是指一個類型。如果`Widget`被使用`Wine`初始化,`Widget`模板中的`MyAllocList<T>::type`指的是一個數據成員,而不是一個類型。在`Wedget`模板中,`MyAllocList<T>::type`是否指的是一個類型忠實地依賴于傳入的`T`是什么,這也是編譯器堅持要求你在類型前面冠以`typename`的原因。 如果你曾經做過模板元編程(`TMP`),你會強烈地額反對使用模板類型參數并在此基礎上修改為其他類型的必要性。例如,給定一個類型`T`,你有可能想剝奪`T`所包含的所有的`const`或引用的修飾符,即你想將`const std::string&`變成`std::string`。你也有可能想給一個類型加上`const`或者將它變成一個左值引用,也就是將`Widget`變成`const Widget`或者`Widget&`。(如果你沒有做過`TMP`,這太糟糕了,因為如果你想成為一個真正牛叉的`C++`程序員,你至少需要對`C++`這方面的基本概念足夠熟悉。你可以同時看一些TMP的例子,包括我上面提到的類型轉換,還有條款23和條款27。) `C++11`給你提供了工具來完成這類轉換的工作,表現的形式是`type traits`,它是`<type_traits>`中的一個模板的分類工具。在這個頭文件中有數十個類型特征,但是并不是都可以提供類型轉換,不提供轉換的也提供了意料之中的接口。給定一個你想競選類型轉換的類型`T`,得到的類型是`std::transformation<T>::type`。例如: ```cpp std::remove_const<T>::type // 從 const T 得到 T std::remove_reference<T>::type // 從 T& 或 T&& 得到 T std::add_lvalue_reference<T>::type // 從 T 得到 T& ``` 注釋僅僅總結了這些轉換干了什么,因此不需要太咬文嚼字。在一個項目中使用它們之前,我知道你會參考準確的技術規范。 無論如何,我在這里不是只想給你大致介紹一下類型特征。反而是因為注意到,類型轉換總是以`::type`作為每次使用的結尾。當你對一個模板中的類型參數(你在實際代碼中會經常用到)使用它們時,你必須在每次使用前冠以`typename`。這其中的原因是`C++11`的類型特征是通過內嵌`typedef`到一個模板化的`struct`來實現的。就是這樣的,他們就是通過使用類型同義技術來實現的,就是我一直在說服你遠不如模板別名的那個技術。 這是一個歷史遺留問題,但是我們略過不表(我打賭,這個原因真的很枯燥)。因為標準委員會姍姍來遲地意識到模板別名是一個更好的方式,對于`C++11`的類型轉換,委員會使這些模板也成為`C++14`的一部分。別名有一個統一的形式:對于`C++11`中的每個類型轉換`std::transformation<T>::type`,有一個對應的`C++14`的模板別名`std::transformation_t`。用例子來說明我的意思: ```cpp std::remove_const<T>::type // C++11: const T -> T std::remove_const_t<T> // 等價的C++14 std::remove_reference<T>::type // C++11: T&/T&& -> T std::remove_reference_t<T> // 等價的C++14 std::add_lvalue_reference<T>::type // C++11: T -> T& std::add_lvalue_reference_t<T> // 等價的C++14 ``` `C++11`的結構在`C++14`中依然有效,但是我不知道你還有什么理由再用他們。即便你不熟悉`C++14`,自己寫一個模板別名也是小兒科。僅僅`C++11`的語言特性被要求,孩子們甚至都可以模擬一個模式,對嗎?如果你碰巧有一份`C++14`標準的電子拷貝,這依然很簡單,因為需要做的即使一些復制和粘貼操作。在這里,我給你開個頭: ```cpp template<class T> using remove_const_t = typename remove_const<T>::type; template<class T> using remove_reference_t = typename remove_reference<T>::type; template<class T> using add_lvalue_reference_t = typename add_lvalue_reference<T>::type; ``` 看到沒有?不能再簡單了。 |要記住的東西| |:--------- | |`typedef`不支持模板化,但是別名聲明支持| |模板別名避免了`::type`后綴,在模板中,`typedef`還經常要求使用`typename`前綴| |`C++14`為`C++11`中的類型特征轉換提供了模板別名|
                  <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>

                              哎呀哎呀视频在线观看