编译期的类型测试
一、使用模板特化机制
如比较两个类型是否一致:
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++程序并在编译期执行的过程”就叫模板元编程。它将工作从运行期转移到编译期,可大大降低执行程序的大小和内存需求,提高运行效率。