本文僅介紹一些技術性較強的traits。由于traits的定義往往重復代碼較多,所以必要時本文僅剖析其底層機制。所有源碼均摘自相應頭文件中,為使源碼簡潔,所有的宏均已展開。由于traits技巧與編譯平臺息息相關,某些平臺可能不支持模板偏特化。
動機
使用traits的動機一般有三種,分派、效率、使某些代碼通過編譯。
分派
下面有一個模板函數,假設一個動物收容組織提供了它,他們接受所有無家可歸的可憐的小動物,于是他們向外界提供了一個函數接受注冊。函數看起來像這樣:
template<class T> // T表示接受的是何種動物
void AcceptAnimals(T animal)
{
... //do something
};
但是,如果他們想將貓和狗分開處理(畢竟飼養一只貓和飼養一只狗并不相同。他們可能會為狗買一根鏈子,而溫順的貓則可能不需要)。一個可行的方法是分別提供兩個函數:AcceptDog和AcceptCat,然而這種解決辦法并不優雅(想想看,注冊者可能既有一只貓又有一只狗,這樣他不得不調用不同的函數來注冊,而且,如果種類還在增多呢,那樣會導致向外提供的接口的增多,注冊者因此而不得不記住那些煩瑣的名字,而這顯然沒有只需記住AccpetAnimal這一個名字簡單)。如果想保持這個模板函數,并將它作為向外界提供的唯一接口,則我們需要某種方式來獲取類型T的特征(trait),并按照不同的特征來采用不同的策略。這里我們有第二個解決辦法:
約定所有的動物類(如class Cat,class Dog)都必須在內部typedef一個表明自己身份的類型,作為標識的類型如下:
struct cat_tag{}; //這只是個空類,目的是激發函數重載,后面會解釋
struct dog_tag{}; //同上
于是,所有狗類都必須像這樣:
class Dog
{
public:
// 類型(身份)標志,表示這是狗類,如果是貓類則為typedef cat_tag type;
typedef dog_tag type;
...
}
然后,動物收容組織可以在內部提供對貓狗分開處理的函數,像這樣:
// 第二個參數為無名參數,只是為了激發函數重載
template<class T>
void Accept(T dog,dog_tag)
{...}
template<class T>
void Accpet(T cat,cat_tag) // 同上
{...}
于是先前的Accept函數可以改寫如下:
template<class T>
void Accept(T animal) //這是向外界提供的唯一接口
{
// 如果T為狗類,則typename T::type就是dog_tag,那么typename T::type()就是創建了一個dog_tag類的臨時對象,根據函數重載的規則,這將調用Accept(T,dog_tag),這正是轉向處理狗的策略。如果T為貓類,則typename T::type為cat_tag,由上面的推導,這將調用Accept(T,cat_tag),即轉向處理貓的策略,typename 關鍵字告訴編譯器T::type是個類型而不是靜態成員。
Accept(animal, typename T::type()); // #1
}
所有類型推導,函數重載,都在編譯期完成,你幾乎不用耗費任何運行期成本(除了創建dog_tag,cat_tag臨時對象的成本,然而經過編譯器的優化,這種成本可能也會消失)就擁有了可讀性和可維護性高的代碼。“但是,等等!”你說:“traits在哪?”,typename T::type其實就是traits,只不過少了一層封裝而已,如果像這樣作一些改進:
template<typename T>
struct AnimalTraits
{
typedef T::type type;
};
于是,#1處的代碼便可以寫成:
Accept(animal, typename AnimalTraits<T>::type());
效率
通常為了提高效率,為某種情況采取特殊的措施是必要的,例如STL里面的copy,原型像這樣:
// 將[first,last)區間內的元素拷貝到以dest開始的地方
template<typename IterIn,typename IterOut>
IterOut copy(IterIn first,IterIn last,IterOut dest){
// ptr_category用來萃取出迭代器的類別以進行適當程度的優化
return copy_opt(first,last,dest, ptr_category(first,dest));
}
copy_opt有兩個版本,其中一個是針對如基本類型的數組作優化的,如果拷貝發生在char數組間,那么根本用不著挨個元素賦值,基于數組在內存中分布的連續性,可以用速度極快的memmove函數來完成。ptr_category有很多重載版本,對可以使用memmove的情況返回一個空類如scalar_ptr的對象以激發函數重載。其原始版本則返回空類non_scalar_ptr的對象。copy_opt的兩個版本于是像這樣:
// 使用memmove
template<typename IterIn,typename IterOut>
IterOut copy(IterIn first,IterIn last,IterOut dest,
scalar_ptr)
{ ...}
// 按部就班的逐個拷貝
template<typename IterIn,typename IterOut>
IterOut copy(IterIn first,IterIn last,IterOut dest,
non_scalar_ptr)
{ ...}
其實通常為了提高效率,還是需要分派。
使某些代碼能通過編譯
這或許令人費解,原來不能通過編譯的代碼,經過traits的作用就能編譯了嗎?是的,考慮std::pair的代碼(為使代碼簡潔,忽略大部分):
template <typename T1, typename T2>
struct pair
{
T1 first;
T2 second;
// 如果T1或T2本身是引用,則編譯錯誤,因為沒有“引用的引用”
pair(const T1 & nfirst, const T2 & nsecond) // #2
:first(nfirst), second(nsecond) { }
};
這里可以使用一個traits(boost庫里面的名字為add_reference)來避免這樣的錯誤。這個traits內含一個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)
...
這對所有的類型都能通過編譯。
boost庫中的traits
boost中的Traits十分完善,可分為如下幾大類:
1. Primary Type Categorisation(初級類型分類)
2. Secondary Type Categorisation(次級類型分類)
3. Type Properties(類型屬性)
4. Relationships Between Types(類型間關系)
5. Transformations Between Types(類型間轉換)
6. Synthesizing Types(類型合成)
7. Function Traits(函數traits)
由于其中一些traits只是簡單的模板偏特化,故不作介紹,本文僅介紹一些技術性較強的traits。由于traits的定義往往重復代碼較多,所以必要時本文僅剖析其底層機制。所有源碼均摘自相應頭文件中,為使源碼簡潔,所有的宏均已展開。由于traits技巧與編譯平臺息息相關,某些平臺可能不支持模板偏特化。這里我們假設編譯器是符合C++標準的。在我的VC7.0上,以下代碼均通過編譯并正常工作。
初級類型分類
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++標準允許整型常量表達式作為模板參數,上面的N就是這樣。這也說明出現在模板偏特化版本中的模板參數(在本例中為typename T,size_t N兩個)個數不一定要跟缺省的(本例中為typename T一個)相同,但出現在類名稱后面的參數個數卻要跟缺省的個數相同(is_array<T[N]>,T[N]為一個參數,與缺省的個數相同)。
使用
is_array<int [10]>::value // true(T=int,N=10)
is_array<int>::value // false(T=int)
is_class(.../is_class.hpp)
定義
// 底層實現,原因是根據不同的編譯環境可能有不同的底層實現,我的編譯環境為VC7.0,其他底層實現從略。
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是一個元函數,提供邏輯與(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
{
// 所有實現都在is_class_imp中
static const bool value = is_class_impl<T>::value;
};
注解
::boost::type_traits::yes_type是一個typedef:
因此sizeof(yes_type)為1.
::boost::type_traits::no_type則是一個struct:
struct no_type
{
char padding[8];
};
因此sizeof(no_type)為8。
這兩個類型一般被用作重載函數的返回值類型,這樣通過檢查返回值類型的大小就知道到底調用了哪個函數,它們的定義位于“boost/type_traits/detail/yes_no_type.hpp”中。
is_class_impl中有兩個static函數,第一個函數僅當模板參數U是類時才能夠被實例化,因為它的參數類型是void(U::*)(void),即指向成員函數的指針。第二個函數具有不定量任意參數列表,C++標準說只有當其它所有的重載版本都不能匹配時,具有任意參數列表(...)的重載版本才會被匹配。所以,如果T為類,則void (T::*)(void)這種類型就存在,所以對is_class_tester<T>(0)的重載決議將是調用第一個函數,因為將0賦給任意類型的指針都是合法的。而如果T不是類,則就不存在void(T::*)(void)這種指針類型,所以第一個函數就不能實例化,這樣,對is_class_tester<T>(0)的重載決議結果只能調用第二個函數。
現在注意#3處的表達式:
sizeof(is_class_tester<T>(0))==sizeof(...::yes_type) // #3
按照上面的推導,如果T為類,is_class_tester<T>(0)實際調用第一個重載版本,返回yes_type,則該表達式求值為true。如果T不是類,則is_class_tester<T>(0)調用第二個重載版本,返回no_type,則該表達式求值為false。這正是我們想要的。
一個值得注意的地方是:在sizeof的世界里,沒有表達式被真正求值,編譯器只推導出表達式的結果的類型,然后給出該類型的大小。
比如,對于sizeof(is_class_tester<T>(0))編譯器實際并不調用函數的代碼來求值,而只關心函數的返回值類型。所以聲明該函數就夠了。另一個值得注意之處是is_class_tester的兩個重載版本都用了模板函數的形式。第一個版本用模板形式的原因是如果不那樣做,而是這樣
static yes_type is_class_tester(void(T::*)(void));
的話,則當T不是類時,該traits將不能通過編譯,原因很簡單,當T不是類時void (T::*)(void)根本不存在。然而,使用模板時,當T不是類時該重載版本會因不能實例化而根本不編譯,C++標準允許不被使用的模板不編譯(實例化)。這樣編譯器就只能使用第二個版本,這正合我們的意思。
而is_class_tester的第二個重載版本為模板則是因為第一個版本是模板,因為在#3處對is_class_tester的調用是這樣的:
is_class_tester<T>(0)
如果第二版本不是模板的話,這樣調用只能解析為對is_class_tester模板函數(即第一個版本)的調用,于是重載解析也就不復存在了。
“等等!”你意識到了一些問題:“模板函數的調用可以不用顯式指定模板參數!”好吧,也就是說你試圖這樣寫:
// 模板
template <class U>
static ...::yes_type is_class_tester(void(U::*)(void));
// 非模板
static ...::no_type is_class_tester(...);
然后在#3標記的那一行這樣調用:
is_class_tester(0) // 原來是is_class_tester<T>(0))
是的,我得承認,這的確構成了函數重載的條件,也的確令人欣喜的通過了編譯,然而結果肯定不是你想要的。你會發現對所有類型T,is_class<T>::value現在都是0了!
也就是說,編譯器總是調用is_class_tester(..);這是因為,當調用的函數的所有重載版本中有一個或多個為模板時,編譯器首先要嘗試進行模板函數實例化而非重載決議,而在嘗試實例化的過程中,編譯器會進行模板參數推導,0的類型被編譯器推導為int(0雖然可以賦給指針,但0的類型不可能被推導為指針類型,因為指針類型可能有無數種,而事實上C++是強類型語言,對象只能屬于某一種類型),而第一個函數的參數類型void (U::*)(void)根本無法與int匹配(因為如果匹配了,那么模板參數U被推導為什么呢?)。所以第一個版本實例化失敗后編譯器只能采用非模板的第二個版本。結果如你所見,是令人懊惱的。然而如果你寫的是is_class_tester<T>(0)你其實是顯式實例化了is_class_tester每一個模板函數(除了那些不能以T為模板參數實例化的),而它們都被列入接受重載決議的侯選單,然后編譯器要做的就只剩下重載決議了。(關于編譯器在含有模板函數的重載版本時是如何進行重載決議的,可參見C++ Primer的Function Templates一節,里面有極其詳細的介紹)。
以上所將的利用函數重載來達到某些目的的技術在type_traits甚至整個boost庫里多處用到。
初級類型分類還有:
is_void is_integral is_float is_pointer is_reference is_union is_enum is_function
請參見boost提供的文檔。
次級類型分類
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;
};
// 偏特化版本,匹配無參數的成員函數
template <class R, class T >
struct is_mem_fun_pointer_impl<R (T::*)() >
{
static const bool value = true;
};
//匹配一個參數的成員函數
template <class R, class T , class T0>
struct is_mem_fun_pointer_impl<R (T::*)( T0) >
{
static const bool value = true;
};
... // 其它版本只是匹配不同參數個數的成員函數的偏特化而已,參見源文件。
template<class T>
struct is_mem_function_pointer
{
static const bool value =
is_mem_fun_pointer_impl<T>::value;
};
注解
假設你有一個類X,你這樣判斷:
is_mem_function_pointer<int (X::*)(int)>::value
則編譯器會先將is_mem_function_pointer的模板參數class T推導為int (X::*)(int),然后將其傳給is_mem_fun_pointer_impl,隨后編譯器尋找后者的偏特化版本中最佳匹配項為:
is_mem_fun_pointer_impl<R(T::*)(T0)>
其中R=int,T=X,T0=int。而該偏特化版本的::value=true。
次級類型分類還有:
is_arithmetic is_fundamental is_object is_scalar is_compound
請參見boost提供的文檔。
類型屬性
is_empty(.../is_empty.hpp)
定義
// 如果T是空類,那么派生類的大小就是派生部分的大小即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
通過比較以上兩個類的大小可以判斷T是否為空類,如果它們大小相等則T為空類。反之則不為空。
這里一個值得注意的地方是:若定義一個空類E,則sizeof(E)為1(這一個字節是用于在內存中唯一標識該類的不同對象。如果sizeof(E)為0,則意味著不同的對象在內存中的位置沒有區別,這顯然有違直觀)。然而如果有另一個非空類繼承自E,那么這一個字節的內存就不需要。也就是說派生類的大小等于派生部分的大小,而非加上一個字節。
// 這個輔助類的作用是:如果T不是類則使用該缺省版本如果T是類則使用下面的偏特化版本。而判斷T是否為類的工作則由上面講過的is_class<>traits來做。
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_cv將T的const volatile屬性去掉,這是因為在作為基類的類型不能有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>::value為true(即T為類)則empty_helper<cvt,is_class<T>::value>::value實際決議為empty_helper<cvt,true>,這將采用偏特化版本#5,則結論出現。
否則T不是類,則采用缺省版本,結果::value為false。
is_polymorphic(.../is_polymorphic.hpp)
is_plymorphic的運作機制基于一個基本事實:一個多態的類里面會有一個虛函數表指針(一般稱為vptr),它指向一個虛函數表(一般稱為vtbl)。后者保存著一系列指向虛函數的函數指針以及運行時類型識別信息。一個虛函數表指針通常占用4個字節(32尋址環境下的所有指針都占用4個字節)。反之,如果該類不是多態,則沒有這個指針的開銷。基于這個原理,我們可以斷定:如果類X不是多態類(沒有vtbl及vptr),則如果從它派生一個類Y,Y中僅含有一個虛函數,這會導致sizeof(Y)>sizeof(X)(這是因為虛函數的首次出現導致編譯器必須在Y中加入vptr的緣故)。反之,如果X原本就是多態類,則sizeof(Y)==sizeof(X)(因為這種情況下,Y中其實已經有了從X繼承而來的vtbl及vptr,編譯器所要做的只是將新增的虛函數納入到vtbl中去)。
定義
// 當T為類時使用這個版本
template <class T>
struct is_polymorphic_imp1
{
typedef typename remove_cv<T>::type ncvT;
// ncvT是將T的const volatile修飾符去掉后的類型,因為public后不能跟這樣的修飾符,該類里沒有虛函數
struct d1 : public ncvT
{
d1();
~d1() // throw();
char padding[256];
};
struct d2 : public ncvT // 在d2中加入一個虛函數
{
d2();
//加入一個虛函數,如果ncvT為非多態則會導致vptr的加入從而多占用4字節
virtual ~d2() // throw();
char padding[256];
};
// 如果T為多態類則value為true
static const bool value =
(sizeof(d2) == sizeof(d1));
};
// 當T并非類時采用這個版本
template <class T>
struct is_polymorphic_imp2
{
// 既然T不是類,那么就不存在多態,所以總是false
static const bool value = false;
};
// 這個selector根據is_class的真假來選擇判斷的方式
template <bool is_class>
struct is_polymorphic_selector
{
// 如果is_class為false則由is_polymorphic_imp2來判斷,這將導致結果總是false
template <class T>
struct rebind
{
typedef is_polymorphic_imp2<T> type; // 使用_imp2
};
};
//當is_class為true時使用該特化版本
template <>
struct is_polymorphic_selector<true> // #7
{
// 如果is_class為true,則由is_polymorphic_imp1<>來作判斷
template <class T>
struct rebind
{
typedef is_polymorphic_imp1<T> type; // 使用_imp1
};
};
// is_polymorphic完全由它實現
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為類,則is_class<T>::value為true,則那一行實際上就是:
typedef is_polymorphic_selector<true> selector;
這將決議為is_polymorphic_selector的第二個重載版本#7,其中的template rebind將判斷的任務交給is_polymorphic_imp1,所以#8行的binder其實就是is_polymorphic_selector<true>::rebind<T>。而#9行的imp_type其實就是is_polymorphic_imp1<T>,結果正如預期。如果T不是類,按照類似的推導過程,最終會推導至is_polymorphic_imp2<T>::value,這正是false。
“嗨!這太煩瑣了!”你抱怨道:“可以簡化!”。我知道,你可能會想到使用boost::ct_if(ct_if是?:三元操作符的編譯期版本,像這樣使用:
typedef
ct_if<CompileTimeBool,TypeIfTrue,TypeIfFalse>::value
result;
則當CompileTimeBool為true時result為TypeIfTrue,否則result為TypeIfFalse。ct_if<>的實現很簡單,模板偏特化而已)。于是你這樣寫:
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環境下的確編譯通過并正常工作,但是有一個小問題:假如T不是class,比如,T是一個int,則編譯器的類型推導會將is_polymorphic_imp1<int>賦給ct_if的第二個模板參數,在這個過程中編譯器會不會實例化is_polymorphic_imp1<int>(或者,換句話說,編譯器會不會去查看它的定義)呢?如果實例化了,那么其內部的struct d1 : public ncvT會不會也跟著實例化為struct d1:public int,如果是這樣,那么將會有編譯期錯誤,因為C++標準不允許有public int這樣的東西出現。事實上我的編譯器沒有報錯,即是說它并沒有去查看is_polymorphic_imp1<int>的定義。
而C++標準實際上也支持這種做法。但boost庫中的做法更為保險,也許是為了應付一些老舊的編譯器。
類型屬性traits還有:
alignment_of is_const is_volatile is_pod has_trivial_constructor等
類型間關系
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
{
// 該轉換操作符當對象為const對象時才起作用
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的底層機制。下面我就為你講解它所仰賴的機制,假設有這樣的類繼承體系:
struct B {};
struct B1 : B {};
struct B2 : B {};
struct D : private B1, private B2 {};
將D*轉換為B1*會導致訪問違規,因為私有基類部分無法訪問,但是后面解釋了這為什么不會發生。
首先來看一些術語:
SC - Standard Conversion
UDC - User-Defined Conversion
一個user-defined轉換序列由一個SC后跟一個UDC后再跟一個SC組成。其中頭尾兩個SC都可以為到自身的轉換(如:D->D),#10處將一個缺省構造的Host()交給bd_helper<B,D>::check函數。
對于static no_type check(B const volatile *, int),我們有如下可行的隱式轉換序列:
Host -> Host const -> B const volatile* (UDC)
或
Host -> D const volatile* (UDC) -> B1 const volatile* / B2 const volatile* -> B const volatile* (SC)
而對于static yes_type check(D const volatile *, T),我們則有如下轉換序列:
Host -> D const volatile* (UDC)
C++標準說,在重載決議中選擇最佳匹配函數時,只考慮標準轉換(SC)序列,而這個序列直到遇到一個UDC為止,對于第一個函數,將Host -> Host const與Host -> Host比較,顯然選擇后者。因為后者是前者的一個真子集。因此,去掉第一個轉換序列我們得到:
C -> D const volatile* (UDC) -> B1 const volatile* / B2 const volatile* -> B const volatile* (SC)
vs.
C -> D const volatile* (UDC)
這里采用選擇最短序列的原則,選擇后者,這表明編譯器甚至根本不需要去考慮向B轉換的多重路徑,或者訪問限制,所以轉換二義性和訪問違規也就不會發生。結論是如果D繼承自B,則選擇yes_type check()。
如果D不是繼承自B,則對于static no_type check(B const volatile *, int)編譯器的給出的轉換為:
C -> C const -> B const volatile*(UDC)
對于static yes_type check(D const volatile *, T)編譯器給出:
C -> D const volatile* (UDC)
這兩個都不錯(都需要一個UDC),然而由于static no_type check(B const volatile *, int)為非模板函數,所以被編譯器選用。結論是如果D并非繼承自B,則選擇no_type check()。
另外,在我的VC7.0環境下,如果將Host的operator B const volatile *() const的const拿掉,則結果將總是false。
可惜這樣的理解并不屬于我,它們來自boost源代碼中的注釋。
is_convertible(boost/type_traits/is_convertible.hpp)
定義
template< typename From >
struct does_conversion_exist
{
template< typename To >
struct result_
{
// 當不存在從From到To的任何轉型時調用它
static no_type _m_check(...);
// 只要轉型存在就調用它
static yes_type _m_check(To);
// 這只是個聲明,所以并不占用空間,且沒有開銷。
static From _m_from;
enum
{
value =
sizeof( _m_check(_m_from) ) == sizeof(yes_type);
};
};
};
// 這是個為void準備的特化版本,因為不能聲明void _m_from,只有void可以向void“轉換”
template<>
struct does_conversion_exist<void>
{
template< typename To >
struct result_
{
enum { value = ::boost::is_void<To>::value };
};
};
// is_convertible完全使用does_conversion_exist作底層機制,所以略去。
注解
does_conversion_exist也使用了與is_class_impl一樣的技術。所以注解從略。該技術最初由Andrei Alexandrescu發明。
最后,Transformations Between Types(類型間轉換),Synthesizing Types(類型合成),Function Traits(函數traits)的機制較為單純,請自行參考boost提供的文檔或頭文件。
traits是泛型世界中的精靈:小巧,精致。traits也是泛型編程中最精微的東西,它們往往仰賴于一些編譯期決議的規則,C++標準,和神奇的模板偏特化。這也導致了它們在不同的平臺上可能有不同表現,更常見的是,在某些平臺上根本無法工作。然而,由于它們的依據是C++標準,而編譯器會越來越符合標準,所以這些問題只是暫時的。traits也是構建泛型世界的基本組件之一,它們往往能使設計變得優雅,精致,甚至完美。
目錄(展開《boost源碼剖析》系列文章)
來自:http://blog.csdn.net/pongba/archive/2004/08/24/83828.aspx