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 };
};