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

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                條款4:知道如何查看類型推導 ========================== 對類型推導結果的查看的工具的選擇和你在軟件開發過程中的相關信息有關系。我們要探討三種可能:在你編寫代碼的時候,在編譯的時候和在運行的時候得到類型推導的信息。 ###IDE編輯器 在IDE里面的代碼編輯器里面當你使用光標懸停在實體之上,常常可以顯示出程序實體(例如變量,參數,函數等等)的類型。舉一個例子,下面的代碼: ```cpp const int theAnswer = 42; auto x = theAnswer; auto y = &theAnswer; ``` 一個IDE的編輯器很可能會展示出`x`的推導的類型是`int`,`y`的類型是`const int*`。 對于這樣的情況,你的代碼必須處在一個差不多可以編譯的狀態,因為這樣可以使得IDE接受這種在IDE內部運行這的一個C++編譯器(或者至少是一個前端)的信息。如果那個編譯器無法能夠有足夠的能力去感知你的代碼并且parse你的代碼然后去執行類型推導,他就無法展示對應推導的類型了。 對于簡單的類型例如`int`,IDE里面的信息是正常的。但是我們隨后會發現,涉及到更加復雜的類型的時候,從IDE里面得到的信息并不一定是有幫助性的。 ###編譯器診斷 一個有效的讓編譯器展示類型的辦法就是故意制造編譯問題。編譯的錯誤輸出會報告會和捕捉到的類型相關錯誤。 假設,舉個例子,我們希望看在上面例子中的`x`和`y`被推導的類型。我們首先聲明一個類模板,但是并不定義這個模板。就像下面優雅的做法: ```cpp template<typename T> // 聲明TD class TD; // TD == "Type Displayer" ``` 嘗試實例化這個模板會導致錯誤信息,因為沒有模板的定義實現。想看`x`和`y`被推導的類型,只要嘗試去使用這些類型去實例化`TD`: ```cpp TD<decltype(x)> xType; // 引起的錯誤 TD<decltype(y)> yType; // 包含了x和y的類型 ``` 我使用的變量名字的形式`variableNameType`是因為這樣有利于輸出的錯誤信息可以幫助我定位我要尋找的信息。對上面的代碼,我的一個編譯器輸出了診斷信息,其中的一部分如下:(我把我們關注的類型信息高亮了(原文中高亮了模板中的`int`和`const int*`,但是Markdown在代碼block中操作粗體比較麻煩,譯文中沒有加粗——譯者注)): error: aggregate 'TD<int> xType' has incomplete type and cannot be defined error: aggregate 'TD<const int *> yType' has incomplete type and cannot be defined 另一個編譯器提供相同的信息,但是格式不太一樣: error: 'xType' uses undefined class 'TD<int>' error: 'yType' uses undefined class 'TD<const int *>' 排除格式的區別,我測試了所有的編譯器都會在這種代碼的技術中輸出有用的錯誤信息。 ###運行時輸出 `printf`到運行的時候可以用來顯示類型信息(這并不是我推薦你使用`printf`的原因),但是它提供了對輸出格式的完全掌控。挑戰就在于你要創造一個你關心的對象的輸出的格式控制展示的textual。“這還不容易,”你會這樣想,“就是用`typeid`和`std::type_info::name`來救場啊。”在后續的對`x`和`y`的類型推導中,你可以發現你可以這樣寫: ```cpp std::cout << typeid(x).name() << '\n'; // display types for std::cout << typeid(y).name() << '\n'; // x and y ``` 這是基于對類似于`x`或者`y`運算`typeid`可以得到一個`std::type_info`對象,`std::type_info`有一個成員函數,`name`可以提供一個C-style的字符串(也就是`const char*`)代表了類型的名字。 調用`std::type_info::name`并不會確定返回有意義的東西,但是實現上是有幫助性質的。幫助是多種多樣的。舉一個例子,GNU和Clang編譯器返回`x`的類型是“`i`”,`y`的類型是“`PKi`”。這些編譯器的輸出結果你一旦學會就可以理解他們,“`i`”意味著“`int`”,“`PK`”意味著“pointer to ~~konst~~ const”(所有的編譯器都支持一個工具,`C++filt`,它可以解析這樣的“亂七八糟”的類型。)微軟的編譯器提供更加直白的輸出:“`int`”對`x`,“`int const*`”對`y`。 因為這些結果對`x`和`y`而言都是正確的,你可能認為類型輸出的問題就此解決了,但是這并不能輕率。考慮一個更加復雜的例子: ```cpp template<typename T> // template function to void f(const T& param); // be called std::vector<Widget> createVec(); // 工廠方法 const auto vw = createVec(); // init vw w/factory return if (!vw.empty()) { f(&vw[0]); // 調用f … } ``` 在代碼中,涉及了一個用戶定義的類型(`Widget`),一個STL容器(`std::vector`),一個`auto`變量(`vw`),這對你的編譯器的類型推導的可視化是非常具有表現性的。舉個例子,想看到模板類型參數`T`和`f`的函數模板參數`param`。 在問題中沒有`typeid`是很直接的。在`f`中添加一些代碼去展示你想要的類型: ```cpp template<typename T> void f(const T& param) { using std::cout; cout << "T = " << typeid(T).name() << '\n'; // 展示T cout << "param = " << typeid(param).name() << '\n'; // 展示param的類型 … } ``` 使用GNU和Clang編譯器編譯會輸出如下結果: T = PK6Widget param = PK6Widget 我們已經知道對于這些編譯器,`PK`意味著“pointer to `const`”,所以比較奇怪的就是數字6,這是在后面跟著的類的名字(`Widget`)的字母字符的長度。所以這些編譯器就告我我們`T`和`param`的類型都是`const Widget*`。 微軟的編譯器輸出: T = class Widget const * param = class Widget const * 三種不同的編譯器都產出了相同的建議性信息,這表明信息是準確的。但是更加仔細的分析,在模板`f`中,`param`的類型是`const T&`。`T`和`param`的類型是一樣的難道不會感到奇怪嗎?舉個例子,如果`T`是`int`,`param`的類型應該是`const int&`——根本不是相同的類型。 悲劇的是,`std::type_info::name`的結果并不可靠。在這種情況下,舉個例子,所有的三種編譯器報告的`param`的類型都是不正確的。更深入的話,它們本來就是不正確的,因為`std::type_info::name`的特化指定了類型會被當做它們被傳給模板函數的時候的按值傳遞的參數。正如條款1所述,這就意味著如果類型是一個引用,他的引用特性會被忽略,如果在忽略引用之后存在`const`(或者`volatile`),它的`const`特性(或者`volatile`特性)會被忽略。這就是為什么`param`的類型——`const Widget * const &`——被報告成了`const Widget*`。首先類型的引用特性被去掉了,然后結果參數指針的`const`特性也被消除了。 同樣的悲劇,由IDE編輯器顯示的類型信息也并不準確——或者說至少并不可信。對之前的相同的例子,一個我知道的IDE的編輯器報告出`T`的類型(我不打算說): ```cpp const std::_Simple_types<std::_Wrap_alloc<std::_Vec_base_types<Widget, std::allocator<Widget> >::_Alloc>::value_type>::value_type * ``` 還是這個相同的IDE編輯器,`param`的類型是: ```cpp const std::_Simple_types<...>::value_type *const & ``` 這個沒有`T`的類型那么嚇人,但是中間的“...”會讓你感到困惑,直到你發現這是IDE編輯器的一種說辭“我們省略所有`T`類型的部分”。帶上一點運氣,你的開發環境也許會對這樣的代碼有著更好的表現。 如果你更加傾向于庫而不是運氣,你就應該知道`std::type_info::name`可能在IDE中會顯示類型失敗,但是Boost TypeIndex庫(經常寫做Boost.TypeIndex)是被設計成可以成功顯示的。這個庫并不是C++標準的一部分,也不是IDE和模板的一部分。更深層的是,事實上Boost庫(在[boost.com](http://boost.com/))是一個跨平臺的,開源的,并且基于一個偏執的團隊都比較喜歡的協議。這就意味著基于標準庫之上使用Boost庫的代碼接近于一個跨平臺的體驗。 這里展示了一段我們使用Boost.TypeIndex的函數`f`精準的輸出類型信息: ```cpp #include <boost/type_index.hpp> template<typename T> void f(const T& param) { using std::cout; using boost::typeindex::type_id_with_cvr; // show T cout << "T = " << type_id_with_cvr<T>().pretty_name() << '\n'; // show param's type cout << "param = " << type_id_with_cvr<decltype(param)>().pretty_name() << '\n'; … } ``` 這個模板函數`boost::typeindex::type_id_with_cvr`接受一個類型參數(我們想知道的類型信息)來正常工作,它不會去除`const`,`volatile`或者引用特性(這也就是模板中的“`cvr`”的意思)。返回的結果是個`boost::typeindex::type_index`對象,其中的`pretty_name`成員函數產出一個`std::string`包含一個對人比較友好的類型展示的字符串。 通過這個`f`的實現,再次考慮之前使用`typeid`導致推導出現錯誤的`param`類型信息: ```cpp std::vector<Widget> createVec(); // 工廠方法 const auto vw = createVec(); // init vw w/factory return if (!vw.empty()) { f(&vw[0]); // 調用f … } ``` 在GNU和Clang的編譯器下面,Boost.TypeIndex輸出(準確)的結果: T = Widget const* param = Widget const* const& 微軟的編譯器實際上輸出的結果是一樣的: T = class Widget const * param = class Widget const * const & 這種接近相同的結果很漂亮,但是需要注意IDE編輯器,編譯器錯誤信息,和類似于Boost.TypeIndex的庫僅僅是一個對你編譯類型推導的一種工具而已。所有的都是有幫助意義的,但是到目前為止,沒有什么關于類型推導法則1-3的替代品。 |要記住的東西| | :--------- | | 類型推導的結果常常可以通過IDE的編輯器,編譯器錯誤輸出信息和Boost TypeIndex庫的結果中得到| | 一些工具的結果不一定有幫助性也不一定準確,所以對C++標準的類型推導法則加以理解是很有必要的|
                  <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>

                              哎呀哎呀视频在线观看