• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            本文僅介紹一些技術(shù)性較強(qiáng)的traits。由于traits的定義往往重復(fù)代碼較多,所以必要時(shí)本文僅剖析其底層機(jī)制。所有源碼均摘自相應(yīng)頭文件中,為使源碼簡(jiǎn)潔,所有的宏均已展開(kāi)。由于traits技巧與編譯平臺(tái)息息相關(guān),某些平臺(tái)可能不支持模板偏特化。

            動(dòng)機(jī)

            使用traits的動(dòng)機(jī)一般有三種,分派、效率、使某些代碼通過(guò)編譯。

             

            分派

            下面有一個(gè)模板函數(shù),假設(shè)一個(gè)動(dòng)物收容組織提供了它,他們接受所有無(wú)家可歸的可憐的小動(dòng)物,于是他們向外界提供了一個(gè)函數(shù)接受注冊(cè)。函數(shù)看起來(lái)像這樣:

             

            template<class T> // T表示接受的是何種動(dòng)物

            void AcceptAnimals(T animal)

            {

            ...  //do something

            };

             

            但是,如果他們想將貓和狗分開(kāi)處理(畢竟飼養(yǎng)一只貓和飼養(yǎng)一只狗并不相同。他們可能會(huì)為狗買(mǎi)一根鏈子,而溫順的貓則可能不需要)。一個(gè)可行的方法是分別提供兩個(gè)函數(shù):AcceptDogAcceptCat,然而這種解決辦法并不優(yōu)雅(想想看,注冊(cè)者可能既有一只貓又有一只狗,這樣他不得不調(diào)用不同的函數(shù)來(lái)注冊(cè),而且,如果種類(lèi)還在增多呢,那樣會(huì)導(dǎo)致向外提供的接口的增多,注冊(cè)者因此而不得不記住那些煩瑣的名字,而這顯然沒(méi)有只需記住AccpetAnimal這一個(gè)名字簡(jiǎn)單)。如果想保持這個(gè)模板函數(shù),并將它作為向外界提供的唯一接口,則我們需要某種方式來(lái)獲取類(lèi)型T的特征(trait),并按照不同的特征來(lái)采用不同的策略。這里我們有第二個(gè)解決辦法:

             

            約定所有的動(dòng)物類(lèi)(class Cat,class Dog)都必須在內(nèi)部typedef一個(gè)表明自己身份的類(lèi)型,作為標(biāo)識(shí)的類(lèi)型如下:

             

            struct cat_tag{}; //這只是個(gè)空類(lèi),目的是激發(fā)函數(shù)重載,后面會(huì)解釋

            struct dog_tag{}; //同上

             

            于是,所有狗類(lèi)都必須像這樣:

             

            class Dog

            {

            public:

              // 類(lèi)型(身份)標(biāo)志,表示這是狗類(lèi),如果是貓類(lèi)則為typedef cat_tag type;

            typedef  dog_tag  type;

              ...

            }

             

            然后,動(dòng)物收容組織可以在內(nèi)部提供對(duì)貓狗分開(kāi)處理的函數(shù),像這樣:

             

            // 第二個(gè)參數(shù)為無(wú)名參數(shù),只是為了激發(fā)函數(shù)重載

            template<class T>

            void Accept(T dog,dog_tag)

            {...}

             

            template<class T>

            void Accpet(T cat,cat_tag) // 同上

            {...}

             

             

            于是先前的Accept函數(shù)可以改寫(xiě)如下:

             

            template<class T>

            void Accept(T animal)  //這是向外界提供的唯一接口

            {

            // 如果T為狗類(lèi),則typename T::type就是dog_tag,那么typename T::type()就是創(chuàng)建了一個(gè)dog_tag類(lèi)的臨時(shí)對(duì)象,根據(jù)函數(shù)重載的規(guī)則,這將調(diào)用Accept(T,dog_tag),這正是轉(zhuǎn)向處理狗的策略。如果T為貓類(lèi),則typename T::typecat_tag,由上面的推導(dǎo),這將調(diào)用Accept(T,cat_tag),即轉(zhuǎn)向處理貓的策略,typename 關(guān)鍵字告訴編譯器T::type是個(gè)類(lèi)型而不是靜態(tài)成員。

            Accept(animal, typename T::type()); // #1

            }

             

            所有類(lèi)型推導(dǎo),函數(shù)重載,都在編譯期完成,你幾乎不用耗費(fèi)任何運(yùn)行期成本(除了創(chuàng)建dog_tag,cat_tag臨時(shí)對(duì)象的成本,然而經(jīng)過(guò)編譯器的優(yōu)化,這種成本可能也會(huì)消失)就擁有了可讀性和可維護(hù)性高的代碼。但是,等等!你說(shuō):“traits在哪?typename T::type其實(shí)就是traits,只不過(guò)少了一層封裝而已,如果像這樣作一些改進(jìn):

             

            template<typename T>

            struct AnimalTraits

            {

            typedef T::type type;

            };

             

            于是,#1處的代碼便可以寫(xiě)成:

             

            Accept(animal, typename AnimalTraits<T>::type());

             

            效率

            通常為了提高效率,為某種情況采取特殊的措施是必要的,例如STL里面的copy,原型像這樣:

             

            // [first,last)區(qū)間內(nèi)的元素拷貝到以dest開(kāi)始的地方

            template<typename IterIn,typename IterOut>

            IterOut copy(IterIn first,IterIn last,IterOut dest){

              // ptr_category用來(lái)萃取出迭代器的類(lèi)別以進(jìn)行適當(dāng)程度的優(yōu)化

            return copy_opt(first,last,dest, ptr_category(first,dest));

            }

             

            copy_opt有兩個(gè)版本,其中一個(gè)是針對(duì)如基本類(lèi)型的數(shù)組作優(yōu)化的,如果拷貝發(fā)生在char數(shù)組間,那么根本用不著挨個(gè)元素賦值,基于數(shù)組在內(nèi)存中分布的連續(xù)性,可以用速度極快的memmove函數(shù)來(lái)完成。ptr_category有很多重載版本,對(duì)可以使用memmove的情況返回一個(gè)空類(lèi)如scalar_ptr的對(duì)象以激發(fā)函數(shù)重載。其原始版本則返回空類(lèi)non_scalar_ptr的對(duì)象。copy_opt的兩個(gè)版本于是像這樣:

             

            // 使用memmove

            template<typename IterIn,typename IterOut>

            IterOut copy(IterIn first,IterIn last,IterOut dest,

            scalar_ptr)

            { ...}

             

            // 按部就班的逐個(gè)拷貝

            template<typename IterIn,typename IterOut>

            IterOut copy(IterIn first,IterIn last,IterOut dest,

             non_scalar_ptr)

            { ...}

             

            其實(shí)通常為了提高效率,還是需要分派。

             

            使某些代碼能通過(guò)編譯

            這或許令人費(fèi)解,原來(lái)不能通過(guò)編譯的代碼,經(jīng)過(guò)traits的作用就能編譯了嗎?是的,考慮std::pair的代碼(為使代碼簡(jiǎn)潔,忽略大部分)

             

            template <typename T1, typename T2>

            struct pair

            {

            T1 first;

              T2 second;

             

            // 如果T1T2本身是引用,則編譯錯(cuò)誤,因?yàn)闆](méi)有“引用的引用

            pair(const T1 & nfirst, const T2 & nsecond) // #2

            :first(nfirst), second(nsecond) { } 

            };

             

            這里可以使用一個(gè)traits(boost庫(kù)里面的名字為add_reference)來(lái)避免這樣的錯(cuò)誤。這個(gè)traits內(nèi)含一個(gè)typedef,如果add_reference<T>T為引用,則typedef T type;如果不是引用,則typedef T& type;這樣#2處的代碼便可改成:

             

            pair(add_reference<const T1>::type nfirst,

            add_reference<const T2>::type nsecond)

              ...

             

            這對(duì)所有的類(lèi)型都能通過(guò)編譯。

             

            boost庫(kù)中的traits

            boost中的Traits十分完善,可分為如下幾大類(lèi):

             

            1. Primary Type Categorisation(初級(jí)類(lèi)型分類(lèi))

            2. Secondary Type Categorisation(次級(jí)類(lèi)型分類(lèi))

            3. Type Properties(類(lèi)型屬性)

            4. Relationships Between Types(類(lèi)型間關(guān)系)

            5. Transformations Between Types(類(lèi)型間轉(zhuǎn)換)

            6. Synthesizing Types(類(lèi)型合成)

            7. Function Traits(函數(shù)traits)

             

            由于其中一些traits只是簡(jiǎn)單的模板偏特化,故不作介紹,本文僅介紹一些技術(shù)性較強(qiáng)的traits。由于traits的定義往往重復(fù)代碼較多,所以必要時(shí)本文僅剖析其底層機(jī)制。所有源碼均摘自相應(yīng)頭文件中,為使源碼簡(jiǎn)潔,所有的宏均已展開(kāi)。由于traits技巧與編譯平臺(tái)息息相關(guān),某些平臺(tái)可能不支持模板偏特化。這里我們假設(shè)編譯器是符合C++標(biāo)準(zhǔn)的。在我的VC7.0上,以下代碼均通過(guò)編譯并正常工作。

             

            初級(jí)類(lèi)型分類(lèi)

            is_array (boost/type_traits/is_array.hpp)

            定義

            // 缺省

            template<typename T>

            struct is_array

            {

            static const bool value=false;

            };

             

            // 偏特化

            template<typename T,size_t N>

            struct is_array<T[N]>

            {

            static const bool value=true;

            };

             

            注解

            C++標(biāo)準(zhǔn)允許整型常量表達(dá)式作為模板參數(shù),上面的N就是這樣。這也說(shuō)明出現(xiàn)在模板偏特化版本中的模板參數(shù)(在本例中為typename T,size_t N兩個(gè))個(gè)數(shù)不一定要跟缺省的(本例中為typename T一個(gè))相同但出現(xiàn)在類(lèi)名稱(chēng)后面的參數(shù)個(gè)數(shù)卻要跟缺省的個(gè)數(shù)相同(is_array<T[N]>T[N]為一個(gè)參數(shù)與缺省的個(gè)數(shù)相同)

             

            使用

            is_array<int [10]>::value  // true(T=int,N=10)

            is_array<int>::value   // false(T=int)

             

            is_class(.../is_class.hpp)

            定義

            // 底層實(shí)現(xiàn),原因是根據(jù)不同的編譯環(huán)境可能有不同的底層實(shí)現(xiàn),我的編譯環(huán)境為VC7.0,其他底層實(shí)現(xiàn)從略。

            template <typename T>

            struct is_class_impl

            {

            template <class U>

            static ...::yes_type is_class_tester(void(U::*)(void));

             

            template <class U> static ...::no_type is_class_tester(...);

             

            // ice_and是一個(gè)元函數(shù),提供邏輯與(AND)操作

            static const bool value =

            ...::ice_and<

            sizeof(is_class_tester<T>(0))==sizeof(...::yes_type), // #3

                  ...::ice_not<...::is_union<T>::value >::value

            >::value

            };

             

            template<typename T>

            struct is_class

            {

            // 所有實(shí)現(xiàn)都在is_class_imp

            static const bool value = is_class_impl<T>::value;

            };

             

            注解

            ::boost::type_traits::yes_type是一個(gè)typedef:

             

            typedef char yes_type;

             

            因此sizeof(yes_type)1.

             

            ::boost::type_traits::no_type是一個(gè)struct:

             

            struct no_type

            {

            char padding[8];

            };

             

            因此sizeof(no_type)8

             

            這兩個(gè)類(lèi)型一般被用作重載函數(shù)的返回值類(lèi)型,這樣通過(guò)檢查返回值類(lèi)型的大小就知道到底調(diào)用了哪個(gè)函數(shù),它們的定義位于“boost/type_traits/detail/yes_no_type.hpp中。

             

            is_class_impl中有兩個(gè)static函數(shù),第一個(gè)函數(shù)僅當(dāng)模板參數(shù)U是類(lèi)時(shí)才能夠被實(shí)例化,因?yàn)樗膮?shù)類(lèi)型是void(U::*)(void),即指向成員函數(shù)的指針。第二個(gè)函數(shù)具有不定量任意參數(shù)列表,C++標(biāo)準(zhǔn)說(shuō)只有當(dāng)其它所有的重載版本都不能匹配時(shí),具有任意參數(shù)列表(...)的重載版本才會(huì)被匹配。所以,如果T為類(lèi),則void (T::*)(void)這種類(lèi)型就存在,所以對(duì)is_class_tester<T>(0)的重載決議將是調(diào)用第一個(gè)函數(shù),因?yàn)閷?/span>0賦給任意類(lèi)型的指針都是合法的。而如果T不是類(lèi),則就不存在void(T::*)(void)這種指針類(lèi)型,所以第一個(gè)函數(shù)就不能實(shí)例化,這樣,對(duì)is_class_tester<T>(0)的重載決議結(jié)果只能調(diào)用第二個(gè)函數(shù)。

             

            現(xiàn)在注意#3處的表達(dá)式:

             

            sizeof(is_class_tester<T>(0))==sizeof(...::yes_type) // #3

             

            按照上面的推導(dǎo),如果T為類(lèi),is_class_tester<T>(0)實(shí)際調(diào)用第一個(gè)重載版本,返回yes_type,則該表達(dá)式求值為true。如果T不是類(lèi),則is_class_tester<T>(0)調(diào)用第二個(gè)重載版本,返回no_type,則該表達(dá)式求值為false。這正是我們想要的。

             

            一個(gè)值得注意的地方是:在sizeof的世界里,沒(méi)有表達(dá)式被真正求值編譯器只推導(dǎo)出表達(dá)式的結(jié)果的類(lèi)型,然后給出該類(lèi)型的大小。

             

            比如,對(duì)于sizeof(is_class_tester<T>(0))編譯器實(shí)際并不調(diào)用函數(shù)的代碼來(lái)求值,而只關(guān)心函數(shù)的返回值類(lèi)型。所以聲明該函數(shù)就夠了。另一個(gè)值得注意之處是is_class_tester的兩個(gè)重載版本都用了模板函數(shù)的形式。第一個(gè)版本用模板形式的原因是如果不那樣做,而是這樣

             

            static yes_type is_class_tester(void(T::*)(void));

             

            的話,則當(dāng)T不是類(lèi)時(shí),該traits將不能通過(guò)編譯,原因很簡(jiǎn)單,當(dāng)T不是類(lèi)時(shí)void (T::*)(void)根本不存在。然而,使用模板時(shí),當(dāng)T不是類(lèi)時(shí)該重載版本會(huì)因不能實(shí)例化而根本不編譯,C++標(biāo)準(zhǔn)允許不被使用的模板不編譯(實(shí)例化)。這樣編譯器就只能使用第二個(gè)版本,這正合我們的意思。

             

            is_class_tester的第二個(gè)重載版本為模板則是因?yàn)榈谝粋€(gè)版本是模板,因?yàn)樵?/span>#3處對(duì)is_class_tester的調(diào)用是這樣的:

             

            is_class_tester<T>(0)

             

            如果第二版本不是模板的話,這樣調(diào)用只能解析為對(duì)is_class_tester模板函數(shù)(即第一個(gè)版本)的調(diào)用,于是重載解析也就不復(fù)存在了。

             

            等等!你意識(shí)到了一些問(wèn)題:模板函數(shù)的調(diào)用可以不用顯式指定模板參數(shù)!好吧,也就是說(shuō)你試圖這樣寫(xiě):

             

            // 模板

            template <class U>

            static ...::yes_type is_class_tester(void(U::*)(void));

             

            // 非模板

            static ...::no_type is_class_tester(...);

             

            然后在#3標(biāo)記的那一行這樣調(diào)用:

             

            is_class_tester(0) // 原來(lái)是is_class_tester<T>(0))

             

            是的,我得承認(rèn),這的確構(gòu)成了函數(shù)重載的條件,也的確令人欣喜的通過(guò)了編譯,然而結(jié)果肯定不是你想要的。你會(huì)發(fā)現(xiàn)對(duì)所有類(lèi)型Tis_class<T>::value現(xiàn)在都是0

             

            也就是說(shuō),編譯器總是調(diào)用is_class_tester(..);這是因?yàn)椋?dāng)調(diào)用的函數(shù)的所有重載版本中有一個(gè)或多個(gè)為模板時(shí),編譯器首先要嘗試進(jìn)行模板函數(shù)實(shí)例化而非重載決議,而在嘗試實(shí)例化的過(guò)程中,編譯器會(huì)進(jìn)行模板參數(shù)推導(dǎo),0的類(lèi)型被編譯器推導(dǎo)為int(0雖然可以賦給指針,但0的類(lèi)型不可能被推導(dǎo)為指針類(lèi)型,因?yàn)橹羔橆?lèi)型可能有無(wú)數(shù)種,而事實(shí)上C++是強(qiáng)類(lèi)型語(yǔ)言,對(duì)象只能屬于某一種類(lèi)型),而第一個(gè)函數(shù)的參數(shù)類(lèi)型void (U::*)(void)根本無(wú)法與int匹配(因?yàn)槿绻ヅ淞耍敲茨0鍏?shù)U被推導(dǎo)為什么呢?)。所以第一個(gè)版本實(shí)例化失敗后編譯器只能采用非模板的第二個(gè)版本。結(jié)果如你所見(jiàn),是令人懊惱的。然而如果你寫(xiě)的是is_class_tester<T>(0)你其實(shí)是顯式實(shí)例化了is_class_tester每一個(gè)模板函數(shù)(除了那些不能以T為模板參數(shù)實(shí)例化的),而它們都被列入接受重載決議的侯選單,然后編譯器要做的就只剩下重載決議了。(關(guān)于編譯器在含有模板函數(shù)的重載版本時(shí)是如何進(jìn)行重載決議的,可參見(jiàn)C++ PrimerFunction Templates一節(jié),里面有極其詳細(xì)的介紹)

             

            以上所將的利用函數(shù)重載來(lái)達(dá)到某些目的的技術(shù)在type_traits甚至整個(gè)boost庫(kù)里多處用到。

             

            初級(jí)類(lèi)型分類(lèi)還有:

             

            is_void is_integral is_float is_pointer is_reference is_union is_enum is_function

             

            請(qǐng)參見(jiàn)boost提供的文檔。

             

            次級(jí)類(lèi)型分類(lèi)

            is_member_function_pointer(.../is_member_function_pointer.hpp)

            定義(.../detail/is_mem_fun_pointer_impl.hpp)

            // 缺省版本

            template <typename T>

            struct is_mem_fun_pointer_impl

            {

            static const bool value = false;

            };

             

            // 偏特化版本,匹配無(wú)參數(shù)的成員函數(shù)

            template <class R, class T  >

            struct is_mem_fun_pointer_impl<R (T::*)() >

            {

            static const bool value = true;

            };

             

            //匹配一個(gè)參數(shù)的成員函數(shù)

            template <class R, class T ,  class T0>

            struct is_mem_fun_pointer_impl<R (T::*)( T0) >

            {

            static const bool value = true;

            };

             

            ... // 其它版本只是匹配不同參數(shù)個(gè)數(shù)的成員函數(shù)的偏特化而已,參見(jiàn)源文件。

             

            template<class T>

            struct is_mem_function_pointer

            {

            static const bool value =

            is_mem_fun_pointer_impl<T>::value;

            };

             

            注解

            假設(shè)你有一個(gè)類(lèi)X,你這樣判斷:

             

            is_mem_function_pointer<int (X::*)(int)>::value

             

            則編譯器會(huì)先將is_mem_function_pointer的模板參數(shù)class T推導(dǎo)為int (X::*)(int),然后將其傳給is_mem_fun_pointer_impl,隨后編譯器尋找后者的偏特化版本中最佳匹配項(xiàng)為:

             

            is_mem_fun_pointer_impl<R(T::*)(T0)>

             

            其中R=int,T=X,T0=int。而該偏特化版本的::value=true

             

            次級(jí)類(lèi)型分類(lèi)還有:

             

            is_arithmetic is_fundamental is_object is_scalar is_compound

             

            請(qǐng)參見(jiàn)boost提供的文檔。

             

            類(lèi)型屬性

            is_empty(.../is_empty.hpp)

            定義

             

            // 如果T是空類(lèi),那么派生類(lèi)的大小就是派生部分的大小即sizeof(int)*256

            template <typename T>

            struct empty_helper_t1

            : public T

            {                                                          

            empty_helper_t1(); 

              int i[256];

            };

             

            struct empty_helper_t2

            {

            int i[256];

            }; // 大小為sizeof(int)*256

             

            通過(guò)比較以上兩個(gè)類(lèi)的大小可以判斷T是否為空類(lèi),如果它們大小相等則T為空類(lèi)。反之則不為空。

             

            這里一個(gè)值得注意的地方是:若定義一個(gè)空類(lèi)E,則sizeof(E)1(這一個(gè)字節(jié)是用于在內(nèi)存中唯一標(biāo)識(shí)該類(lèi)的不同對(duì)象。如果sizeof(E)0,則意味著不同的對(duì)象在內(nèi)存中的位置沒(méi)有區(qū)別,這顯然有違直觀)。然而如果有另一個(gè)非空類(lèi)繼承自E,那么這一個(gè)字節(jié)的內(nèi)存就不需要。也就是說(shuō)派生類(lèi)的大小等于派生部分的大小,而非加上一個(gè)字節(jié)。

             

            // 這個(gè)輔助類(lèi)的作用是:如果T不是類(lèi)則使用該缺省版本如果T是類(lèi)則使用下面的偏特化版本。而判斷T是否為類(lèi)的工作則由上面講過(guò)的is_class<>traits來(lái)做。

            template <typename T, bool is_a_class = false> 

            struct empty_helper

            {

            static const bool value = false;

            };

             

            template <typename T>

            struct empty_helper<T, true> // #5

            {

            static const bool value =

            (sizeof(empty_helper_t1<T>) == sizeof(empty_helper_t2));

            };

             

            template <typename T>

            struct is_empty_impl

            {

            // remove_cvTconst volatile屬性去掉,這是因?yàn)樵谧鳛榛?lèi)的類(lèi)型不能有const/volatile修飾。

            typedef typename remove_cv<T>::type cvt;

             

            static const bool value =

            ice_or<

                  empty_helper<cvt, is_class<T>::value>::value, // #4

            BOOST_IS_EMPTY(cvt)

            >::value;

            };

             

            注解

            #4處,如果is_class<T>::valuetrue(T為類(lèi))empty_helper<cvt,is_class<T>::value>::value實(shí)際決議為empty_helper<cvt,true>,這將采用偏特化版本#5則結(jié)論出現(xiàn)。

             

            否則T不是類(lèi),則采用缺省版本,結(jié)果::valuefalse

             

            is_polymorphic(.../is_polymorphic.hpp)

            is_plymorphic的運(yùn)作機(jī)制基于一個(gè)基本事實(shí):一個(gè)多態(tài)的類(lèi)里面會(huì)有一個(gè)虛函數(shù)表指針(一般稱(chēng)為vptr),它指向一個(gè)虛函數(shù)表(一般稱(chēng)為vtbl)。后者保存著一系列指向虛函數(shù)的函數(shù)指針以及運(yùn)行時(shí)類(lèi)型識(shí)別信息。一個(gè)虛函數(shù)表指針通常占用4個(gè)字節(jié)(32尋址環(huán)境下的所有指針都占用4個(gè)字節(jié))。反之,如果該類(lèi)不是多態(tài),則沒(méi)有這個(gè)指針的開(kāi)銷(xiāo)。基于這個(gè)原理,我們可以斷定:如果類(lèi)X不是多態(tài)類(lèi)(沒(méi)有vtblvptr),則如果從它派生一個(gè)類(lèi)YY中僅含有一個(gè)虛函數(shù),這會(huì)導(dǎo)致sizeof(Y)>sizeof(X)(這是因?yàn)樘摵瘮?shù)的首次出現(xiàn)導(dǎo)致編譯器必須在Y中加入vptr的緣故)。反之,如果X原本就是多態(tài)類(lèi),則sizeof(Y)==sizeof(X)(因?yàn)檫@種情況下,Y中其實(shí)已經(jīng)有了從X繼承而來(lái)的vtblvptr,編譯器所要做的只是將新增的虛函數(shù)納入到vtbl中去)

             

            定義

            // 當(dāng)T為類(lèi)時(shí)使用這個(gè)版本

            template <class T>

            struct is_polymorphic_imp1

            {

            typedef typename remove_cv<T>::type ncvT;

             

            // ncvT是將Tconst volatile修飾符去掉后的類(lèi)型,因?yàn)?/span>public后不能跟這樣的修飾符,該類(lèi)里沒(méi)有虛函數(shù)

            struct d1 : public ncvT

            {                

            d1();

            ~d1() // throw();

            char padding[256];

            };

             

            struct d2 : public ncvT  // d2中加入一個(gè)虛函數(shù)

            {

            d2();

             

            //加入一個(gè)虛函數(shù),如果ncvT為非多態(tài)則會(huì)導(dǎo)致vptr的加入從而多占用4字節(jié)

            virtual ~d2() // throw();

            char padding[256];

            };

             

            // 如果T為多態(tài)類(lèi)則valuetrue

            static const bool value =

            (sizeof(d2) == sizeof(d1));

            };

             

            // 當(dāng)T并非類(lèi)時(shí)采用這個(gè)版本

            template <class T>

            struct is_polymorphic_imp2 

            {

            // 既然T不是類(lèi),那么就不存在多態(tài),所以總是false

            static const bool value = false;

            };

             

            // 這個(gè)selector根據(jù)is_class的真假來(lái)選擇判斷的方式

            template <bool is_class>

            struct is_polymorphic_selector

            {

              // 如果is_classfalse則由is_polymorphic_imp2來(lái)判斷,這將導(dǎo)致結(jié)果總是false

            template <class T>

            struct rebind

            {

            typedef is_polymorphic_imp2<T> type; // 使用_imp2

            };

            };

             

            //當(dāng)is_classtrue時(shí)使用該特化版本

            template <>

            struct is_polymorphic_selector<true> // #7

            {

              // 如果is_classtrue,則由is_polymorphic_imp1<>來(lái)作判斷

            template <class T>

            struct rebind

            {

            typedef is_polymorphic_imp1<T> type; // 使用_imp1

            };

            };

             

            // is_polymorphic完全由它實(shí)現(xiàn)

            template <class T>

            struct is_polymorphic_imp 

            {

              // 選擇selector

            typedef

            is_polymorphic_selector<is_class<T>::value> selector; // #6

             

            typedef typename selector::template rebind<T> binder; // #8

             

            typedef typename binder::type imp_type; // #9

             

            static const bool value = imp_type::value;

            };

             

            注解

            #6處如果T為類(lèi),則is_class<T>::valuetrue,則那一行實(shí)際上就是:

             

            typedef is_polymorphic_selector<true> selector;

             

            這將決議為is_polymorphic_selector的第二個(gè)重載版本#7,其中的template rebind將判斷的任務(wù)交給is_polymorphic_imp1,所以#8行的binder其實(shí)就是is_polymorphic_selector<true>::rebind<T>。而#9行的imp_type其實(shí)就是is_polymorphic_imp1<T>結(jié)果正如預(yù)期。如果T不是類(lèi),按照類(lèi)似的推導(dǎo)過(guò)程,最終會(huì)推導(dǎo)至is_polymorphic_imp2<T>::value,這正是false

             

            嗨!這太煩瑣了!你抱怨道:可以簡(jiǎn)化!。我知道,你可能會(huì)想到使用boost::ct_if(ct_if?:三元操作符的編譯期版本,像這樣使用:

             

            typedef

            ct_if<CompileTimeBool,TypeIfTrue,TypeIfFalse>::value

            result;

             

             

            則當(dāng)CompileTimeBooltrue時(shí)resultTypeIfTrue,否則resultTypeIfFalsect_if<>的實(shí)現(xiàn)很簡(jiǎn)單,模板偏特化而已)。于是你這樣寫(xiě):

             

            typedef typename boost::ct_if<

            is_class<T>::value,

                      is_polymorphic_imp1<T>,

                      is_polymorphic_imp2<T>,

                    >::type

            imp_type;

             

            static const bool value = imp_type::value;

             

            這在我的VC7.0環(huán)境下的確編譯通過(guò)并正常工作,但是有一個(gè)小問(wèn)題:假如T不是class,比如,T是一個(gè)int,則編譯器的類(lèi)型推導(dǎo)會(huì)將is_polymorphic_imp1<int>賦給ct_if的第二個(gè)模板參數(shù),在這個(gè)過(guò)程中編譯器會(huì)不會(huì)實(shí)例化is_polymorphic_imp1<int>(或者,換句話說(shuō),編譯器會(huì)不會(huì)去查看它的定義)呢?如果實(shí)例化了,那么其內(nèi)部的struct d1 : public ncvT會(huì)不會(huì)也跟著實(shí)例化為struct d1:public int,如果是這樣,那么將會(huì)有編譯期錯(cuò)誤,因?yàn)?/span>C++標(biāo)準(zhǔn)不允許有public int這樣的東西出現(xiàn)。事實(shí)上我的編譯器沒(méi)有報(bào)錯(cuò),即是說(shuō)它并沒(méi)有去查看is_polymorphic_imp1<int>的定義。

             

            C++標(biāo)準(zhǔn)實(shí)際上也支持這種做法。但boost庫(kù)中的做法更為保險(xiǎn),也許是為了應(yīng)付一些老舊的編譯器。

             

            類(lèi)型屬性traits還有:

             

            alignment_of is_const is_volatile is_pod has_trivial_constructor

             

            類(lèi)型間關(guān)系

            is_base_and_derived(boost/type_traits/is_base_and_derived.hpp)

            定義

            template<typename B, typename D>

            struct bd_helper

            {

            template<typename T>

            static type_traits::yes_type check(D const volatile *, T);

             

            static type_traits::no_type check(B const volatile *, int);

            };

             

            template<typename B, typename D>

            struct is_base_and_derived_impl2

            {

            struct Host

            {

            // 該轉(zhuǎn)換操作符當(dāng)對(duì)象為const對(duì)象時(shí)才起作用

            operator B const volatile *() const;

             

            operator D const volatile *();

            };

             

            static const bool value =

            sizeof(bd_helper<B,D>::check(Host(), 0)) // #10

            == sizeof(type_traits::yes_type);

            };

             

            以上就是is_base_and_derived的底層機(jī)制。下面我就為你講解它所仰賴(lài)的機(jī)制,假設(shè)有這樣的類(lèi)繼承體系:

             

            struct B {};

            struct B1 : B {};

            struct B2 : B {};

             

            struct D : private B1, private B2 {};

             

            D*轉(zhuǎn)換為B1*會(huì)導(dǎo)致訪問(wèn)違規(guī),因?yàn)樗接谢?lèi)部分無(wú)法訪問(wèn),但是后面解釋了這為什么不會(huì)發(fā)生。

             

            首先來(lái)看一些術(shù)語(yǔ):

             

            SC  - Standard Conversion

            UDC - User-Defined Conversion

             

            一個(gè)user-defined轉(zhuǎn)換序列由一個(gè)SC后跟一個(gè)UDC后再跟一個(gè)SC組成。其中頭尾兩個(gè)SC都可以為到自身的轉(zhuǎn)換(如:D->D)#10處將一個(gè)缺省構(gòu)造的Host()交給bd_helper<B,D>::check函數(shù)。

             

            對(duì)于static no_type check(B const volatile *, int)我們有如下可行的隱式轉(zhuǎn)換序列:

             

            Host -> Host const -> B const volatile* (UDC)

            Host -> D const volatile* (UDC) -> B1 const volatile* / B2 const volatile* -> B const volatile* (SC)

             

            而對(duì)于static yes_type check(D const volatile *, T),我們則有如下轉(zhuǎn)換序列:

             

            Host -> D const volatile* (UDC)

             

            C++標(biāo)準(zhǔn)說(shuō),在重載決議中選擇最佳匹配函數(shù)時(shí),只考慮標(biāo)準(zhǔn)轉(zhuǎn)換(SC)序列,而這個(gè)序列直到遇到一個(gè)UDC為止,對(duì)于第一個(gè)函數(shù),將Host -> Host constHost -> Host比較,顯然選擇后者。因?yàn)楹笳呤乔罢叩囊粋€(gè)真子集。因此,去掉第一個(gè)轉(zhuǎn)換序列我們得到:

             

            C -> D const volatile* (UDC) -> B1 const volatile* / B2 const volatile* -> B const volatile* (SC)

             

            vs.

             

            C -> D const volatile* (UDC)

             

            這里采用選擇最短序列的原則,選擇后者,這表明編譯器甚至根本不需要去考慮向B轉(zhuǎn)換的多重路徑,或者訪問(wèn)限制,所以轉(zhuǎn)換二義性和訪問(wèn)違規(guī)也就不會(huì)發(fā)生。結(jié)論是如果D繼承自B,則選擇yes_type check()

             

            如果D不是繼承自B,則對(duì)于static no_type check(B const volatile *, int)編譯器的給出的轉(zhuǎn)換為:

             

            C -> C const -> B const volatile*(UDC)

             

            對(duì)于static yes_type check(D const volatile *, T)編譯器給出:

             

            C -> D const volatile* (UDC)

             

            這兩個(gè)都不錯(cuò)(都需要一個(gè)UDC),然而由于static no_type check(B const volatile *, int)為非模板函數(shù),所以被編譯器選用。結(jié)論是如果D并非繼承自B,則選擇no_type check()

             

            另外,在我的VC7.0環(huán)境下,如果將Hostoperator B const volatile *() constconst拿掉,則結(jié)果將總是false

             

            可惜這樣的理解并不屬于我,它們來(lái)自boost源代碼中的注釋。

             

            is_convertible(boost/type_traits/is_convertible.hpp)

            定義

            template< typename From >

            struct does_conversion_exist

            {

            template< typename To >

            struct result_

            {

              // 當(dāng)不存在從FromTo的任何轉(zhuǎn)型時(shí)調(diào)用它

            static no_type _m_check(...); 

             

            // 只要轉(zhuǎn)型存在就調(diào)用它

            static yes_type _m_check(To);

             

            // 這只是個(gè)聲明,所以并不占用空間,且沒(méi)有開(kāi)銷(xiāo)。

            static From _m_from;

                 

            enum

            {

            value =

            sizeof( _m_check(_m_from) ) == sizeof(yes_type);

            };

            };

            };

             

            // 這是個(gè)為void準(zhǔn)備的特化版本,因?yàn)椴荒苈暶?/span>void _m_from,只有void可以向void“轉(zhuǎn)換

            template<>

            struct does_conversion_exist<void>

            {

              template< typename To >

            struct result_

            {

            enum { value = ::boost::is_void<To>::value };

              };

            };

             

            // is_convertible完全使用does_conversion_exist作底層機(jī)制,所以略去。

             

            注解

            does_conversion_exist也使用了與is_class_impl一樣的技術(shù)。所以注解從略。該技術(shù)最初由Andrei  Alexandrescu發(fā)明。

             

            最后,Transformations Between Types(類(lèi)型間轉(zhuǎn)換)Synthesizing Types(類(lèi)型合成)Function Traits(函數(shù)traits)的機(jī)制較為單純,請(qǐng)自行參考boost提供的文檔或頭文件。

             

            traits是泛型世界中的精靈:小巧,精致。traits也是泛型編程中最精微的東西,它們往往仰賴(lài)于一些編譯期決議的規(guī)則,C++標(biāo)準(zhǔn),和神奇的模板偏特化。這也導(dǎo)致了它們?cè)诓煌钠脚_(tái)上可能有不同表現(xiàn),更常見(jiàn)的是,在某些平臺(tái)上根本無(wú)法工作。然而,由于它們的依據(jù)是C++標(biāo)準(zhǔn),而編譯器會(huì)越來(lái)越符合標(biāo)準(zhǔn),所以這些問(wèn)題只是暫時(shí)的。traits也是構(gòu)建泛型世界的基本組件之一,它們往往能使設(shè)計(jì)變得優(yōu)雅,精致,甚至完美。

             

            目錄(展開(kāi)《boost源碼剖析》系列文章)

             

            來(lái)自:http://blog.csdn.net/pongba/archive/2004/08/24/83828.aspx

            posts - 94, comments - 138, trackbacks - 0, articles - 94

            Copyright © RichardHe

            国内精品久久久久影院老司| 麻豆AV一区二区三区久久| 久久精品人妻中文系列| 国内精品伊人久久久久AV影院| 精品久久久久久国产免费了| 波多野结衣久久| 日本一区精品久久久久影院| 久久久久国产一区二区三区| 亚洲va中文字幕无码久久 | 久久国产高清字幕中文| 精品国产青草久久久久福利 | 久久午夜伦鲁片免费无码| 国产福利电影一区二区三区,免费久久久久久久精 | 性高湖久久久久久久久AAAAA| 久久精品人人做人人爽97| 久久99精品免费一区二区| 久久久久久久久久久久中文字幕 | 久久综合狠狠综合久久综合88| 亚洲精品高清久久| 色8久久人人97超碰香蕉987| 久久精品国产亚洲7777| 国内精品久久久久久久97牛牛| 日韩亚洲国产综合久久久| 精品一区二区久久久久久久网站| 欧美精品九九99久久在观看| 久久久久一区二区三区| 午夜天堂av天堂久久久| 亚洲精品无码久久毛片| 亚洲综合精品香蕉久久网97| 久久婷婷五月综合97色一本一本| 色诱久久av| 久久国产成人午夜aⅴ影院| 精品永久久福利一区二区| 2021国产精品午夜久久| 久久亚洲国产精品123区| 久久综合久久综合九色| AV无码久久久久不卡蜜桃| 亚洲va久久久噜噜噜久久男同 | 久久电影网| 伊人久久大香线焦综合四虎| 99久久中文字幕|