SGI STL类型萃取: __type_traits
__type_traits基本概念
iterator_traits是STL针对迭代器加以规范,用来萃取迭代器特性的机制。SGI STL把这种技法扩大到迭代器以外的地方,于是有了__type_traits。(双下划线表示是内部所用,不在STL标准规范内)
iterator_traits 提供机制,用于萃取迭代器的5个特性:iterator_category(迭代器类型)、value_type(所指对象类型)、difference_type(迭代器做算术运算类型)、pointer(指针类型)、reference(引用类型)。
SGI中,__type_traits位于<type_traits.h>,它提供了一种机制,用于萃取不同类型属性:在编译期就能完成函数派生决定(function dispatch)。这对于编写template很有帮助,比如,当我们准备对一个“元素类型未知”的数组执行copy操作时,如果能事先知道元素类型是否有一个trivial copy constructor(平凡的拷贝构造函数),就能帮助我们决定是否使用更高效的memcpy()或memmove()。
__type_traits内嵌类型
程序中,可以这样应用__type_traits
__type_traits<T>::has_trivial_default_constructor
__type_traits<T>::has_trivial_copy_constructor
__type_traits<T>::has_trivial_assignment_operator
__type_traits<T>::has_trivial_destructor
__type_traits<T>::is_POD_type // POD: Plain Old Data
上面的式子,返回的是“真”或“假”,便于我们决定采用上面策略,但结果不是bool值,而是有着真/假属性的“对象”,因为我们希望编译器可以在编译期,就利用响应结果来进行参数推导,而编译器只有面对class object形式的参数,才会做参数类型推导。因此,上面式子应该传回这样的东西:
// 用作真、假的标记类
struct __true_type {};
struct __false_type {};
这两个空白的class没有任何成员,仅作为标记类,不会带来任何负担。
为了达成上面5个式子,在__type_traits内定义一些typedefs,其(返回)值不是__true_type,就是__false_type。下面是SGI的做法:
template<class type>
struct __type_traits
{
typedef __true_type this_dummy_member_must_be_first;
/* 不要移除这个成员,它通知“有能力自动将__type_traits特化的编译器说,
我们现在看到的这个__type_traits template是特殊的。这是为了确保万一编译器也使用一个名为__type_traits而其实与此处定义无任何关联的template时,所有事情仍将顺利运作”*/
/* 以下条件应该遵守,因为编译器有可能自动为各类型专属的__type_traits特化版本:
- 你可以重新安排以下的成员次序
- 你可以移除以下任何成员
- 绝对不可以将以下成员重命名而没有改变编译器中对应名称
- 新加入的成员会被视为一般成员,除非你在编译期中加上适当支持
*/
typedef __false_type has_trivial_default_constructor;
typedef __false_type has_trivail_copy_constructor;
typedef __false_type has_trivail_assignment_constructor;
typedef __false_type has_trivail_destructor;
typedef __false_type is_POD_type; // POD: Plain Old Data
};
为什么SGI把所有__type_traits的内嵌类型都定义为__false_type?
因为SGI定义出最保守的值,然后,再针对每个标量类型(scalar types)设计适当的__type_traits特化版本。
__type_traits可以接受任何类型的参数,5个typedefs将经由以下管道获得实值:
- 一般具现(general instantiation),内含对所有类型都必定有效的保守值。比如,上面各个has_trivial_xxx类型都被定义为__false_type,就是对所有类型都有效的保守值。
- 经过声明的特化版本,例如<type_traits.h>中对所有C++标量类型(scalar types)(指bool/char/int/float/double等基本类型)都提供了对应特化声明。
- 某些编译器自动为所有类型提供适当的特化版本。
__type_traits针对C++标量类型的特化版
/* 以下针对C++基本型别char, signed char, unsignedchar, short, unsigned short,
int, unsigned int, long, unsigned long, float, double, long double 提供特化版本.
注意, 每个成员的值都是__true_type, 表示这些型别都可采用最快速方式(如memcpy)来进行拷贝(copy)或赋值(assign) */
# define __STL_TEMPLATE_NULL template<>
__STL_TEMPLATE_NULL struct __type_traits<bool> {
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;
};
#endif /* __STL_NO_BOOL */
__STL_TEMPLATE_NULL struct __type_traits<char> {
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;
};
__STL_TEMPLATE_NULL struct __type_traits<signed char> {
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;
};
__STL_TEMPLATE_NULL struct __type_traits<unsigned char> {
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;
};
__STL_TEMPLATE_NULL struct __type_traits<short> {
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;
};
__STL_TEMPLATE_NULL struct __type_traits<unsigned short> {
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;
};
__STL_TEMPLATE_NULL struct __type_traits<int> {
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;
};
__STL_TEMPLATE_NULL struct __type_traits<unsigned int> {
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;
};
__STL_TEMPLATE_NULL struct __type_traits<long> {
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;
};
__STL_TEMPLATE_NULL struct __type_traits<unsigned long> {
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;
};
...
__type_traits在STL中的应用
__type_traits在SGI STL中应用很广泛,举几个例子,前面这篇文章也提到过SGI STL内存基本处理工具:uninitialized_copy/uninitialized_fill/uninitialized_fill_n
,也有POD类型介绍。本文只挑选跟__type_traits有关的重点讲。
uniitialized_fill_n()全局函数
// __type_traits应用: uninitialized_fill_n()
template<class ForwardIterator, class Size, class T>
inline ForwardIterator uninitialized_fill_n(ForwardIterator first, Size n, const T& x) {
return __uninitialized_fill_n(first, n, x, value_type(first));
}
uninitialized_fill_n()函数以x为初始化元素,自迭代器first开始构造n个元素。为求取最大效率,首先用value_type()萃取出迭代器first的value type,再用__type_traits 判断该型别是否为POD类型:
// __type_traits萃取T的is_POD_type特性,判断是否为POD类型
// 利用__type_traits判断该型别是否为POD类型
template<class ForwardIterator, class Size, class T, class T1>
inline ForwardIterator __uninitialized_fiil_n(ForwardIterator first, Size n, const T& x, T1*) {
typedef typename __type_traits<T1>::is_POD_type is_POD;
return __uninitialized_fiil_n_aux(first, n, x, is_POD);
}
下面就“是否为POD类型”采取最适当的措施:
// 如果不是POD类型, 就派送(dispatch)到这里
template<class ForwardIterator, class Size, class T>
ForwardIterator __uninitialized_fill_n_aux(ForwardIterator first, Size n, const T& x, __false_type) {
...
}
// 如果是POD类型, 就会派送(dispatch)到这里.
template<class ForwardIterator, class Size, class T>
inline ForwardIterator ___uninitialized_fill_n_aux_(ForwardIterator first, Size n, const T& x, __true_type) {
...
}
// 以下定义于<stl_algobase.h>中的fill_n()
template<class OutputIterator, class Size, class T>
OutputIterator fill_n(OutputIterator first, Size n, const T& value) {
...
}
负责对象析构的destroy()全局函数
// destroy()第一个版本, 接受一个指针
template<class T>
inline void destroy(T* pointer) {
...
}
// destroy()第二个版本, 接受两个迭代器. 次函数设法找出元素的数值类型,
// 进而利用__type_traits<>求取最适当措施
template<class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last) {
__destroy(first, last, value_type(first));
}
// __type_traits 萃取T的has_trivial_destructor特性, 判断是否为平凡析构函数
// 判断元素的数值类型(value type)是否有trivial destructor
template<class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
__destroy_aux(first, last, trivial_destructor());
}
// 如果元素的数值类型(value type)有non-trivial destructor, 则派送(dispatch)到这里
template<class ForwardIterator>
inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
...
}
// 如果元素的数值类型(value type)有trivial destructor, 则派送(dispatch)到这里
template<class ForwardIterator>
inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __true_type) {
}
copy()全局函数
copy()全局函数用于拷贝。
// 利用__type_traits萃取T的has_trivial_copy_constructor特性,判断是否为平凡copy构造函数
// 拷贝一个数组, 其元素为任意型别, 视情况采用最有效率的拷贝手段
template<class T> inline void copy(T* source, T* destination, int n) {
copy(source, destination, n, typename __type_traits<T>::has_trivial_copy_constructor());
}
// 拷贝一个数组, 其元素型别拥有non-trivial copy constructors, 则dispatch到这里
template<class T> void copy(T* source, T* destination, int n, __false_type) {
// ...
}
// 拷贝一个数组, 其元素型别拥有trivial copy constructors, 则dispatch到这里
// 可借助memcpy()完成工作
template<class T> void copy(T* source, T* destination, int n, __true_type) {
// ...
}
自定义__type_traits特化版
通常,不应该直接使用__type_traits,STL内部使用。正如SGI <type_traits.h>内部有这样的声明:
/* NOTE: This is an internal header file, included by other STL headers.
* You should not attempt to use it directly.
*/
因为,编译器通常会自动为class生成这些特性,这些特性取决于自定义class是否包含trivial default constructor,trivial copy constructor,trivial assignment operator,或者trivial destructor。但有些古老的编译器,可能并不能为class生成这些特性,导致即使是POD类型,但萃取出来的特性依然是__false_type。此时,需要自行设计一个__type_traits特化版本,明确告诉编译器该class具有哪些特性:
// 假设自定义class名为Shape
// 针对Shape设计的__type_traits特化版本
template<> struct __type_traits<Shape> {
typedef __true_type has_trivial_default_constructor; // 告诉编译器Shape拥有trivial default constructor
typedef __false_type has_trivial_copy_constructor;
typedef __false_type has_trivial_assignment_constructor;
typedef __false_type has_trivial_destructor;
typedef __false_type is_POD_type;
};
如何判断一个class什么时候有自己的non-trivial default constructor,non-trivial copy constructor,non-trivial assignment operator,non-trivial destructor?
一个简单判断原则:如果class内含指针成员,并且进行了动态内存配置,那么该class就需要实现出自己的non-trivial-xxx。