我们知道Traits是C++语言的一种高级特性。STL首先利用Traits技术对迭代器的特性做出规范,制定出iterator_traits(参考:STL算法如何萃取(traits)迭代器型别(value_type)?)。后来SGI STL把它应用在迭代器以外的地方, 就有了type_traits。
函数、类或者一些封装的通用算法中的某些部分会因为数据类型不同而导致处理或逻辑不同(而我们又不希望因为数据类型的差异而修改算法本身的封装时)。traits会是一种很好的解决方案。
由于模板的泛型特性,并不是全部类型都能适应统一泛型定义,如果我们为每种类型都添加上特化处理,可以想象得到,代码会膨胀成怎样。另外,对于不同的类定义,可能需要定义不定的成员函数,如对于包含指针(指向堆内存)的自定义类,需要重新定义拷贝构造、拷贝赋值、析构函数,对于不包含指针的类,这些函数则无需定义和调用,否则会引起性能的损耗。
Traits 是一种 “可用于编译器在编译时根据型别作判断” 的泛型技术,像在执行期根据数值执行判断一样。
不要像用switch一样枚举各个类型,用traits用模板实现。
type traits是算法用来获取对象的一些特征信息,比如说是不是class,是不是function,有没有const、signed、volatile这些修饰符,有没有无用的构造函数之类的技术,根据对象的这些信息就可以分别实现相关的操作。
1 类型特性判断
先来看C++模板编程和编译器的参数推导功能(编译器只有面对类类型参数才会进行参数推导):
#include template struct is_void{ static const bool value = false; // 所有类型value = false};template <> struct is_void{ static const bool value = true; // void类型进行了特化,使value = true};int main(){ std::cout<::value; // 输出0,所有非void类型都会输出0 std::cout<::value; // 输出1 return 0;}
加上一个中间层,能够进行特定类型识别。
C++11中,实现了很多的Type Traits的模板类:
如is_const:
#include #include int main(){ int a; const int b = 3; std::cout << std::is_const::value << std::endl; // 1 std::cout << std::is_const::value << std::endl; // 0}
is_const实现代码(利用模板匹配特性):
/// is_const template // 泛型版本 struct is_const : public false_type { }; template // 特化版本 struct is_const<_tp const>: public true_type { };
两个版本的父类false_type和true_type是两个helper class,其定义如下:
// integral_constant,提供一个编译期常量,类似enum和static const变量方式template // 推导出bool类型及其值struct integral_constant{ static constexpr _Tp value = __v; typedef _Tp value_type; typedef integral_constant<_tp __v> type; constexpr operator value_type() const noexcept { return value; } // 用于类型转换 constexpr value_type operator()() const noexcept { return value; } // 用于仿函数};// The type used as a compile-time boolean with true value.typedef integral_constant true_type; // true_type::value=1// The type used as a compile-time boolean with false value.typedef integral_constant false_type;
value_type 表示值的类型。
value表示值。
type 表示自己, 因此可以用::type::value来获取值。
true_type和false_type两个特化类用来表示bool值类型的traits,很多traits类都需要继承它们。
如果不想细看代码,也可以简单地说,true_type和false_type就是包含一个静态类成员value的类模板,其静态成员一个为true,一个为false,仅此而已。
我们不能将参数设为bool值, 因为需要在编译期就决定该使用哪个函数, 所以需要利用函数模板的参数推导机制(编译器只有面对类类型参数才会进行参数推导), 将__true_type和__false_type表现为一个空类, 就不会带来额外的负担, 又能表示真假, 还能在编译时类型推导就确定执行相应的函数。
对于is_const,通过特化,如果我们使用const类型作为模板is_const类型参数,则可以获得其常量静态成员value的值为true(1)。这是因为模板在实例化的时候选择了“版本2”。反过来,如果模板实例化到“版本1”,则value常量静态成员为false(0)。
Integral_constant使用实例:
#include #include // #include using namespace std;template // 推导出bool类型及其值struct Integral_constant{ static constexpr _Tp value = __v; typedef _Tp value_type; typedef Integral_constant<_tp __v> type; constexpr operator value_type() const noexcept { return value; } // 用于类型转换 constexpr value_type operator()() const noexcept { return value; } // 用于仿函数};// The type used as a compile-time boolean with true value.typedef Integral_constant True_type; // True_type::value=1// The type used as a compile-time boolean with false value.typedef Integral_constant False_type;template struct factorial : Integral_constant::value>{};template <>//特化struct factorial<0> : Integral_constant{};int main(){ typedef Integral_constant IC; cout << "value " << IC::value << endl; // 111 cout << "value_type " << typeid(IC::value_type).name() << endl; // i cout << "type " << typeid(IC::type).name() << endl; // St17Integral_constantIiLi111EE IC ic; int c1 = ic; //调用的operator value_type() cout << c1 << endl; // 111 int c2 = ic();//调用的 operator()() cout << c1 << endl; // 111 cout << Integral_constant::value <::value << endl; // 0 cout << factorial<5>::value<
类型萃取雏形:
#include using namespace std;struct True_type {};struct False_type {}; struct A{};struct B{};template // type_traitsstruct type_traits { typedef False_type has_xxx; // 默认为False_type};template <> // 特化Astruct type_traits { typedef True_type has_xxx;}; template <> // 特化Bstruct type_traits { typedef False_type has_xxx;}; template void test(T t) { // test()调用_test() typedef typename type_traits::has_xxx has_x; _test(has_x());};void _test(True_type) { // _test()版本1 cout << "1" << endl;}void _test(False_type) { // _test()版本2 cout << "0" << endl;}int main() { struct A a; struct B b; test(a); // 输出1 test(b); // 输出0 test(1); // 输出0 test(3.5); // 输出0 return 0;}// type traits用来萃取元素特性,如果元素具有某个性质则do_something,否则do_otherthing。// 这个例子里对类类型A、B进行了特化,只有A类型里has_xxx(某个性质)为true_type,// 向函数test()传递参数T时,type_traits进行特性萃取,将T中的has_xxx 赋予别名has_x,// 而在类型A中对true_type赋予别名has_xxx,所以这里的has_x 就是true_type类型,调用函数_test(),// 函数_test()有两个版本,根据参数进行匹配,参数为true_type类型,输出1。// 调用test(b)、test(1)、test(3.5)输出0是一样的道理。
再来看is_pod:
#include #include templatebool is_pod(T) { return std::is_pod::value; // plain old data, C primitive type}int main(){ int a; std::cout << std::is_pod::value<<:endl std::cout std::is_pod>::value<<:endl std::cout is_pod>
我们可以看看g++4.8.1中POD的定义:
// is_pod// Could use is_standard_layout && is_trivial instead of the builtin.templatestruct is_pod: public integral_constant{ };
这里的__is_pod就是编译器内部的intrinsic。所以说,并非所有的Type Traits都能够使用元编程的手段来实现。C++语言设计者在实践中进行了一些考量,让部分的Type Traits实现为了intrinsic,简单地说,就是要编译器辅助来计算出其值。总的来说,Type Traits就是通过元编程的手段,以及编译器的辅助来实现的。
2 类型萃取与函数派送机制
类型萃取使用模板技术来萃取类型(包含自定义类型和内置类型)的某些特性,用以判断该类型是否含有某些特性,从而在泛型算法中来对该类型进行特殊的处理用来提高效率或者其他。
例如在STL中的destory算法根据函数的参数类型的特性是否有trivial destructor来选择对应的策略来进行destory,如果为内置类型,则不调用该类型的destructor,这样更有效率。否则对迭代器范围内的对象调用destructor来进行destory。
这就是使用类型萃取能提供一种根据类型的某些属性在编译时期进行函数派送的机制。
当容器进行范围destoy的时候,其函数接受first和last的迭代器,若[first,last)范围内所有的元素都调用析构函数,但这个类型T的析构又是无关痛痒的,则会损耗效率。 type_trais可以判别该类型T的的析构函数是否无关痛痒,若是(true_type),则什么都不做,否则调用其析构函数:
// 接受两个迭代器, 以__type_trais<> 判断是否有traival destructor template inline void __destroy(ForwardIterator first, ForwardIterator last, T*) { typedef typename __type_traits::has_trivial_destructor trivial_destructor; __destroy_aux(first, last, trivial_destructor());}// non-travial destructor template inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) { for ( ; first < last; ++first) destroy(&*first);}// travial destructortemplate inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}
在最上层的destory函数通过value_type全局函数来对迭代器萃取迭代器所指向的对象的类型,然后调用__destory,该函数通过类型萃取来判断该类型是否含有trivial destructor来在编译时期进行函数派送。函数派送机制通过模板的编译机制和模板函数的重载来避免在函数代码使用if和else语句或者switch来判断是否有trivial destructor从而进行函数派送,避免了效率损失和代码的混乱。
3 SGI-STL中type traits可以萃取的类型属性
在SGI-STL中,可以萃取的类型属性如下:
has_trivial_default_constructorhas_trivial_copy_constructorhas_trivial_assignment_operatorhas_trivial_destructoris_POD_type
Type Traits实现的基础是函数模板+偏特化+编译器辅助,对特定类型做出特定的定义:
Type Traits首先定义了_true_type和_false_type这两个structure。然后给出了泛化和针对int和double的两个特化版本。可以看出,因为int和double都是Plain Old Data(POD)类型,所以它们的default_constructor, copy_constructor, assignment_operator, destructor 这些东西都不重要,其实根本就不需要,所以它们的has_trivial_xxxx都被定义为_true_type。而泛化版本的这些缺省都是 _false_type。这样,当算法询问一个 POD类型的对象有没有copy_contructor的时候, 就知道答案为否。
我们看看对于一个比较简单的class(不含指针),它的这些has_trival_xxxx会返回什么呢?我们可以测试一下这个Foo类:
class Foo{ private: int d1, d2;};
测试结果
__has_trivial_assign 1
__has_trivial_copy 1
__has_trivial_constructor 1
__has_trivial_destructor 1
这个符合预期,因为class foo里面没有指针,所以上面4个函数都不重要,C++编译器给它们提供的缺省函数就够了。
我们再测试一下list,返回结果为
__has_trivial_assign 0
__has_trivial_copy 0
__has_trivial_constructor 0
__has_trivial_destructor 0
这个也符合预期, 因为list里面有指针嘛。
type_traits的源代码实现如下:
/** * 用来标识真/假对象,利用type_traits响应结果来进行参数推导, * 而编译器只有面对class object形式的参数才会做参数推导, * 这两个空白class不会带来额外负担 */struct __true_type{};struct __false_type{};/** * type_traits结构体设计 */template struct __type_traits{ // 不要移除这个成员 // 它通知能自动特化__type_traits的编译器, 现在这个__type_traits template是特化的 // 这是为了确保万一编译器使用了__type_traits而与此处无任何关联的模板时 // 一切也能顺利运作 typedef __true_type this_dummy_member_must_be_first; // 以下条款应当被遵守, 因为编译器有可能自动生成类型的特化版本 // - 你可以重新安排的成员次序 // - 你可以移除你想移除的成员 // - 一定不可以修改下列成员名称, 却没有修改编译器中的相应名称 // - 新加入的成员被当作一般成员, 除非编译器提供特殊支持 typedef __false_type has_trivial_default_constructor; typedef __false_type has_trivial_copy_constructor; typedef __false_type has_trivial_assignment_operator; typedef __false_type has_trivial_destructor; typedef __false_type is_POD_type;};// 特化类型:// char, signed char, unsigned char,// short, unsigned short// int, unsigned int// long, unsigned long// float, double, long double/** * 以下针对C++内置的基本数据类型提供特化版本, * 使其具有trivial default constructor, * copy constructor, assignment operator, destructor并标记其为POD类型 */__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对char的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对unsigned char的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对short的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对unsigned short的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对int的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对unsigned int的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对long的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对unsigned long的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对float的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对double的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};//针对long double的特化版本__STL_TEMPLATE_NULL struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};// 针对指针提供特化template struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};// 针对char *, signed char *, unsigned char *提供特化struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};struct __type_traits{ typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type;};
附:
ref:
roufoo:C++ Type Traits的学习
Coding_Reading:STL源码剖析——type traits编程技法
-End-