Traits 是一种 “可用于编译器根据型别作判断” 的泛型技术,像在执行期根据数值执行判断一样。
Traits 好处:可以在 ”型别确立当时“意外的其他地点做出与型别相关的判断。这会让代码变得比较干净,更具可读性,而且更好维护。
1 实作出 Pointer Traits
// 边界标记 NullType 和 EmptyType class NullType; struct EmptyType {}; template <typename T> class TypeTraits { private: template <class U> struct PointerTraits // 不是指针,且该型别不能使用 { enum { result = false }; typedef NullType PointeeType; }; template <class U> struct PointerTraits<U*> // 针对任意指针型别 { enum { result = true }; typedef U PointeeType; // T 是个指针型别,求得 T 所指型别,NOT Pointer!!! }; public: enum { isPointer = PointerTraits<T>::result }; typedef typename PointerTraits<T>::PointeeType PointeeType; // typename 不可少!! };
同理,可用 template traits 判断 pointers to members
template <typename T> class TypeTraits { private: // pointers to members template <class U> struct PToMTraits { enum { result = false }; }; template <class U, class V> struct PToMTraits < U V::* > { enum { result = true }; }; // stripping qualifiers (卸除饰词) template <class U> struct UnConst { typedef U Result; }; template <class U> struct UnConst <const U> { typedef U Result; }; public: enum { isMemberPointer = PToMTraits<T>::result }; };
2 优化的参数型别
在泛型代码中,任意给定一个型别 T,什么是”将 T 对象传入函数当做参数“的最有效做法 ?
一般而言,最有效的方法是在传入一个精巧型别时采用 by reference 传递方式,而面对纯量型别时采用 by value 传递方式。
纯量型别由数值型别、枚举型别、指针、指向成员之指针组成。
精巧型别由自定义的类型struct 或者 class 组成。
精巧型别应避免额外暂时对象带来的额外开销(构造函数和析构函数的额外调用动作);纯量型别应避免 reference 带来的间接性所造成的额外开销。
注意:C++ 不允许 references to references。因此,如果 T 已经是一个 reference,千万别对它再加上 reference。
3 卸除饰词
template <typename T> class TypeTraits { private: // stripping qualifiers (卸除饰词) template <class U> struct UnConst { typedef U Result; }; template <class U> struct UnConst <const U> { typedef U Result; }; public: typedef typename UnConst<T>::Result NonConstType; };
4 运用 TypeTraits
enum CopyAlgoSelector { Conservative, Fast }; // Conservative routine-works for any type template <typename InIt, typename OutIt> OutIt CopyImpl(InIt first, InIt last, OutIt result, Int2Type<Conservative>) { for (; first != last; ++first, ++result) *result = *first } // Fast routine-works only for pointers to raw data template <typename InIt, typename OutIt> OutIt CopyImpl(InIt first, InIt last, OutIt result, Int2Type<Fast>) { const size_t n = last - first; memcpy(result, first, n*sizeof(*first)); return result + n; } template <typename InIt, typename OutIt> OutIt Copy(InIt first, InIt last, OutIt result) { typedef TypeTraits<InIt>::PointeeType SrcPointee; typedef TypeTraits<OutIt>::PointeeType DestPointee; enum { copyAlgo = TypeTraits<InIt>::isPointer && TypeTraits<OutIt>::isPointer && TypeTraits<SrcPointee>::isStdFundamental && // 将在 typelist中介绍 TypeTraits<DestPointee>::isStdFundamental && sizeof(SrcPointee) == sizeof(DestPointee) ? Fast : Conservative }; return CopyImpl(first, last, result, Int2Type<copyAlgo>); }
enum copyAlgo 会自动选择某个算法, 逻辑:如果迭代器 都是指针,而且都指向基本型别,而且所指型别的大小一样,那么就使用 memcpy。
如果你这样做:
int*p1 = ... int* p2 =.. usigned int* p3 = .. Copy(p1, p2, p3); // Copy() 调用快速版本,虽然“源端类型” 和 “目标端类型”并不形同。
假设你有一个 C struct ,由基本类型组成的数据,就是所谓的plain old data 或称 POD 结构。C++ 可对 POD 进行 bitwise copy(位逐一拷贝)动作,但Copy() 却无法判断出操作对象是否为 POD,因此调用慢速版本。你只能这样:
template <typename T> struct SupportsBitwiseCopy { enum { result = TypeTraits<T>::isStddFundamental }; }; template <typename InIt, typename OutIt> OutIt Copy(InIt first, InIt last, OutIt result, int2Type<true>) { typedef TypeTraits<InIt>::PointeeType SrcPointee; typedef TypeTraits<OutIt>::PointeeType DestPointee; enum { useFast = TypeTraits<InIt>::isPointer && TypeTraits<OutIt>::isPointer && SupportsBitwiseCopy<SrcPointee>::result && // 将在 typelist中介绍 SupportsBitwiseCopy<DestPointee>::result && sizeof(SrcPointee) == sizeof(DestPointee) ? Fast : Conservative }; return CopyImpl(first, last, result, Int2Type<useFast>); } // 某 (MyType)POD 型别的特化 template <> struct SupportsBitwiseCopy < MyType > { enum { result = true }; };