編譯期的類型測試
一、使用模板特化機制
如比較兩個類型是否一致:
VC8及以後版本支持了type traits機制:
add_const Class // Makes const type from type. add_cv Class // Makes const/volatile type from type. add_pointer Class // Makes pointer to type from type. add_reference Class // Makes reference to type from type. add_volatile Class // Makes volatile type from type. aligned_storage Class // Makes suitably aligned type. alignment_of Class // Gets alignment of type. common_type Class conditional Class decay Class enable_if Class extent Class // Gets an array dimension. has_nothrow_assign Class // Tests whether the type does not throw on assign. has_nothrow_constructor Class // Tests whether the type does not throw on default construction. has_nothrow_copy Class // Tests whether the type does not throw on copy construction. has_nothrow_copy_constructor Class has_nothrow_default_constructor Class has_trivial_assign Class // Tests whether the type has trivial assign. has_trivial_constructor Class // Tests whether the type has trivial default constructor. has_trivial_copy Class // Tests whether the type has trivial copy constructor. has_trivial_copy_constructor Class has_trivial_default_constructor Class has_trivial_destructor Class // Tests whether the type has trivial destructor. has_virtual_destructor Class // Tests whether the type has virtual destructor. is_abstract Class // Tests whether the type is abstract class. is_arithmetic Class // Tests whether the type is arithmetic. is_array Class // Tests whether the type is array. is_base_of Class // Tests whether one type is the base of another. is_class Class // Tests whether the type is a class. is_compound Class // Tests whether the type is not scalar. is_const Class // Tests whether the type is const. is_convertible Class // Tests whether one type is convertible to another. is_empty Class // Tests whether the type is an empty class. is_enum Class // Tests whether the type is an enumeration. is_floating_point Class // Tests whether the type is floating-point. is_function Class // Tests whether the type is a function type. is_fundamental Class // Tests whether the type is void or arithmetic. is_integral Class // Tests whether the type is integral. is_lvalue_reference Class is_member_function_pointer Class // Tests whether the type is a pointer to a member function. is_member_object_pointer Class // Tests whether the type is a pointer to a member object. is_member_pointer Class // Tests whether the type is a pointer to a member. is_object Class // Tests whether the type is an object type. is_pod Class // Tests whether the type is a POD. is_pointer Class // Tests whether the type is a pointer. is_polymorphic Class // Tests whether the type has a virtual function. is_reference Class // Tests whether the type is a reference. is_rvalue_reference Class is_same Class // Tests whether two types are the same. is_scalar Class // Tests whether the type is scalar. is_signed Class // Tests whether the type is a signed integer. is_standard_layout Class is_union Class // Tests whether the type is a union. is_unsigned Class // Tests whether the type is an unsigned integer. is_void Class // Tests whether the type is void. is_volatile Class // Tests whether the type is volatile. make_signed Class make_unsigned Class rank Class // Gets the number of array dimensions. remove_all_extents Class // Makes non-array type from array type. remove_const Class // Makes non-const type from type. remove_cv Class // Makes non-const/volatile type from type. remove_extent Class // Makes element type from array type. remove_pointer Class // Makes type from a pointer to type. remove_reference Class // Makes non-reference type from type. remove_volatile Class // Makes non-volatile type from type. integral_constant Class // Makes integral constant from type and value. |
二、使用Traits Classes表現類型信息
STL中有個advance函數,它的作用是將某個迭代器向前或向後移動移動某個給定的距離。
template<class _InIt, class _Diff>
void advance(_InIt& _Where, _Diff _Off)
這個函數就是執行 _Where += _Off 的動作,但是實際上只有隨機訪問迭代器才支持+=操作,對於其他類型的迭代器只能反覆使用++或--操作,共_Off次。
因此,我們首先想到這樣處理:
template<class _InIt, class _Diff>
void advance(_InIt& _Where, _Diff _Off)
{
if(_Where is a random access iterator)
{
_Where += _Off; // 針對隨機訪問迭代器,使用+=運算
}
else // 對於其他類型迭代器,重複使用++或--
{
if(_Off >= 0)
{ while(_Off--) ++_Where; }
else
{ while(_Off++) --_Where; }
}
}
迭代器是模板類,不同的類型模板其實就是一個不同的類型,如何確定迭代器類型呢?
STL通過Traits classes類表現迭代器的類型信息:
1、STL封裝的五種迭代器類型,對這五種類型使用卷標結構加以確認;
// ITERATOR TAGS
struct input_iterator_tag
{ // identifying tag for input iterators
};
struct output_iterator_tag
{ // identifying tag for output iterators
};
struct forward_iterator_tag: public input_iterator_tag, output_iterator_tag
{ // identifying tag for forward iterators
};
struct bidirectional_iterator_tag: public forward_iterator_tag
{ // identifying tag for bidirectional iterators
};
struct random_access_iterator_tag: public bidirectional_iterator_tag
{ // identifying tag for random-access iterators
};
2、爲每個迭代器打上該類型標籤:
template<class _Myvec>
class _Vector_iterator : public _Vector_const_iterator<_Myvec>
{ // iterator for mutable vector
public:
typedef random_access_iterator_tag iterator_category;
};
template<class _Mylist>
class _List_iterator : public _List_const_iterator<_Mylist>
{ // iterator for mutable list
public:
typedef bidirectional_iterator_tag iterator_category;
};
因此,對於advance傳遞的迭代器,可以通過_InIt::iterator_category確認迭代器類型信息。然而僅僅如此並不能使advance對於傳遞的指針類型生效。
3、定義迭代器的Traits classes類,使advance能夠處理指針類型數據
// TEMPLATE CLASS iterator_traits
template<class _Iter>
struct iterator_traits
{ // get traits from iterator _Iter
typedef typename _Iter::iterator_category iterator_category;
typedef typename _Iter::value_type value_type;
typedef typename _Iter::difference_type difference_type;
typedef difference_type distance_type; // retained
typedef typename _Iter::pointer pointer;
typedef typename _Iter::reference reference;
};
template<class _Ty>
struct iterator_traits<_Ty *>
{ // get traits from pointer
typedef random_access_iterator_tag iterator_category;
typedef _Ty value_type;
typedef ptrdiff_t difference_type;
typedef ptrdiff_t distance_type; // retained
typedef _Ty *pointer;
typedef _Ty& reference;
};
這樣,迭代器的類型信息就可以確定了,advance函數的處理僞代碼就可以這樣實現了:
template<class _InIt, class _Diff>
void advance(_InIt& _Where, _Diff _Off)
{
if(typeid(std::iterator_traits<_InIt>::iterator_category)
== typeid(std::random_access_iterator_tag))
{
_Where += _Off;
}
else
{
if(_Off >= 0)
{ while(_Off--) ++_Where; }
else
{ while(_Off++) --_Where; }
}
}
然而,std::iterator_traits<_InIt>::iterator_category是可在編譯期確定的類型,使用if語句卻是在運行期確定。爲什麼將可以在編譯器完成的事情放在運行期呢?這樣既浪費執行時間也造成可執行文件的膨脹。
4、使用重載機制,實現編譯器的類型確定;
template<class _InIt, class _Diff>
void _Advance(_InIt& _Where, _Diff _Off, input_iterator_tag)
{ // increment iterator by offset, input iterators
for (; 0 < _Off; --_Off) ++_Where;
}
template<class _FI, class _Diff>
void _Advance(_FI& _Where, _Diff _Off, forward_iterator_tag)
{ // increment iterator by offset, forward iterators
for (; 0 < _Off; --_Off) ++_Where;
}
template<class _BI, class _Diff>
void _Advance(_BI& _Where, _Diff _Off, bidirectional_iterator_tag)
{ // increment iterator by offset, bidirectional iterators
for (; 0 < _Off; --_Off) ++_Where;
for (; _Off < 0; ++_Off) --_Where;
}
template<class _RI, class _Diff>
void _Advance(_RI& _Where, _Diff _Off, random_access_iterator_tag)
{ // increment iterator by offset, random-access iterators
_Where += _Off;
}
這樣,advance函數的實現就是這樣的了:
template<class _InIt, class _Diff>
void advance(_InIt& _Where, _Diff _Off)
{
_Advance(_Where, _Off, std::iterator_traits<_InIt>::iterator_category());
}
總結:
1)定義一系列的卷標結構,併爲一族類型指定(typedef)各卷標結構標識類型信息;
2)使用模板和模板特化技術定義Traits 類,使得“類型信息”在編譯器可用;
3)建立一組重載函數或函數模板(如_Advance),彼此間的差異只在於Traits參數。令每個函數的實現代碼與其接受的Traits信息相對應, 實現了編譯期的類型檢查;
4)建立一個控制函數或函數模板(如advance),它調用上述那些重載函數並傳遞Traits class所提供的信息。
附:這種“編寫基於模板的C++程序並在編譯期執行的過程”就叫模板元編程。它將工作從運行期轉移到編譯期,可大大降低執行程序的大小和內存需求,提高運行效率。