现在的位置: 首页 > 综合 > 正文

模板之TypeTraits

2018年03月30日 ⁄ 综合 ⁄ 共 3360字 ⁄ 字号 评论关闭
文章目录

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

抱歉!评论已关闭.