什么是trait和policy:
用一个例子来说明:
AccumutionTraits头文件:
#ifndef ACCUMUTIONTRAITS
#define ACCUMUTIONTRAITS
//与T相关的Trait都被定义在这里了
template<typename T>
class AccumutionTraits;
//char
template<>
class AccumutionTraits<char>{
public:
//定义个T有关的类型
typedef int AccT;
//定义和T有关的总和类型
static AccT zero(){
return 0;
}
};
//short
template<>
class AccumutionTraits<short>{
public:
typedef int AccT;
static AccT zero(){
return 0;
}
};
//int
template<>
class AccumutionTraits<int>{
public:
typedef long AccT;
static AccT zero(){
return 1;
}
};
#endif
SumPolicy头文件:
#ifndef SUMPOLICY
#define SUMPOLICY
class SumPolicy{
public:
template<typename T1,typename T2>
static void accumulate(T1 & total,T2 const & value){
total += value;
}
};
class MultPolicy{
public:
template<typename T1, typename T2>
static void accumulate(T1 & total, T2 const & value){
total *= value;
}
};
#endif
测试源文件:
#include<iostream>
#include"AccumutionTraits.h"
#include"SumPolicy.h"
using namespace std;
template<typename T,
typename T_Policy = SumPolicy/*policy类*/,
typename T_Trait = AccumutionTraits<T>/*属于T的Trait类*/
>
inline
typename T_Trait::AccT accum(T const * beg, T const * end){
typename T_Trait::AccT total = T_Trait::zero();
while (beg != end){
T_Policy::accumulate(total, *beg);
++beg;
}
return total;
}
void main(){
int arrNumber[] = { 1, 2, 3, 4, 5 };
cout << "Accum:" << accum<int, MultPolicy>(&arrNumber[0], &arrNumber[5]) / 5 << endl;
char arrName[] = "templates";
int length = sizeof(arrName)-1;
cout << "Accum:" << accum<char,SumPolicy,AccumutionTraits<short>>(&arrName[0], &arrName[length]) / length << endl;
}
分析:
template<typename T,
typename T_Policy = SumPolicy/*policy类*/,
typename T_Trait = AccumutionTraits<T>/*属于T的Trait类*/
>
其中T_Policy可以看做accum泛型函数的可配置行为!T_Trait可以看做模板参数的额外属性!当我们调用accum泛型函数的时候,可以指定相应的trait类和policy类(但是一般trait类是不会变的——在一些特殊情况下才会被替换,但是policy倒是会经常被替换!)
其中trait的实现主要依赖于全局特化,而policy的实现主要依赖于类的内部静态成员函数模板accumulate
trait类和policy的总体表述:
1)trait类表述了模板参数的一些自然的额外属性
2)policy表述了泛型函数和泛型类的一些可配置行为
trait类的特点:
1)trait可以是fixed trait(不需要通过函数模板参数进行传递的trait)
2)trait参数通常都具有很自然的缺省值(该缺省值很少被改写,或者是不能被改写的)
3)trait参数可以紧密依赖一个或多个主参数
4)trait通常都是用trait模板来实现的
policy类的特点:
1)如果不以模板参数的形式进行传递的话,policy class几乎不起作用
2)policy参数通常不需要具有缺省值,而且通常都是显式指定此参数
3)policy参数和属于一个模板对的其他模板参数通常都是正交的
4)policy class一般包含了成员函数
5)policy既可以用类来实现,也可以用类模板来实现
类型函数:
普通函数的功能:传递一个值,返回一个值
类型函数的功能:传递一个类型,返回一个类型(类型函数使用类模板来实现的!)
示例:
ElementT头文件:
#ifndef ELEMENTT
#define ELEMENTT
#include<vector>
#include<list>
#include<deque>
using namespace std;
template<typename C>
class ElementT{
public:
typedef typename C::value_type element_value_Type;
typedef typename C::value_type & element_reference_Type;
};
//vector
template<typename T>
class ElementT<vector<T>>{
public:
typedef T element_value_Type;
typedef T & element_reference_Type;
};
//list
template<typename T>
class ElementT<list<T>>{
public:
typedef T element_value_Type;
typedef T & element_reference_Type;
};
//deque
template<typename T>
class ElementT<deque<T>>{
public:
typedef T element_value_Type;
typedef T & element_reference_Type;
};
#endif
测试源文件:
#include<iostream>
#include"ElementT.h"
using namespace std;
template<typename C>
typename ElementT<C>::element_value_Type//传递一个容器C,取得容器C的元素类型
sum_of_elements(C const & c);
template<typename C>
typename ElementT<C>::element_reference_Type//传递一个C,取得容器C的元素引用类型
sum_of_elements(C const & c);
void main() {
}
分析:其中ElementT的作用就相当于一个函数,给ElementT“传递”一个类型,ElementT就能”返回“一个类型!
判断一个类型是否是class类型(包括类class类型)
示例:(其中使用了SFINAE原则和重载解析规则)
IsClass头文件:
#ifndef ISCLASST
#define ISCLASST
template<typename T>
class IsClassT{
private:
typedef char One;
typedef struct{ char a[2]; } Two;
//test函数模板1
//如果是class类型的话,就会匹配这个模板函数,从0到指针的转换比匹配省略号更优先
template<typename C>
static One test(int C::*);
//test函数模板2
//只有真的不是类的时候,才会匹配这个省略号参数的函数
template<typename C>
static Two test(...);
public:
enum{ yes = sizeof(test<T>(0)) == 1 };
enum{ no = !yes };
};
#endif
测试源文件:
#include<iostream>
#include"IsClassT.h"
using namespace std;
template<typename T>
void check(){
if (IsClassT<T>::yes){
cout << "Is class Type!" << endl;
}
else{
cout << "Is not class Type!" << endl;
}
}
class MyClass{
};
struct MyStruct{
};
union MyUnion{
};
enum E{e1}e;
void main(){
check<MyClass>();
check<MyStruct>();
check<MyUnion>();
check<E>();
check<int>();
/*
输出结果:
Is class Type!
Is class Type!
Is class Type!
Is not class Type!
Is not class Type!
*/
}
分析:
对于头文件中test函数模板1,只有当实例化test函数模板的模板参数是class类型(包括类class类型)的时候,test函数模板1才会被实例化出来(其余的情况下int C::*将导致替换错误,不会被实例化!——SFINAE原则!)
当IsClassT<T>::yes中的T是class(包括类class类型)的时候:此时test函数模板1和test函数模板2都可以被实例化出来,但是由于重载解析规则:int转换到指针类型的匹配精确度比省略号的匹配精确度高!于是当T是class类型的时候,将实例化这个test函数模板1!
当IsClassT<T>::yes中的T是非class(包括类class类型)的时候:此时实例化test函数模板1将导致替换错误,只能实例化test函数模板2!
以上便是IsClass类模板可以判断某一个类型是否为class类型的主要原理!
SFINAE原则(substitution-failure-is-not-an-error替换失败并非错误!):
#include<iostream>
using namespace std;
//函数模板1
template<typename T>
void FUN(int T::*){
cout << "void FUN(int T::*)" << endl;
}
//函数模板2
template<typename T>
void FUN(...){
cout << "void FUN(...)" << endl;
}
//自定义的Class
class MyClass{
};
void main()
{
FUN<int>(0);//显式实例化1
FUN<MyClass>(0);//显式实例化2
/*
输出结果:
void FUN(...)
void FUN(int T::*)
*/
}
注意:SFINAE问题只发生在显式指定模板实参的时候,SFINAE支持了函数模板的重载!
分析:
1)显式实例化1:这里当用int显式实例化的时候,会考虑所有的模板,当考虑到函数模板1的时候,将发生一个替换错误:int int::*,显然int没有成员指针一说,于是,这里的实例化因为替换动作而产生了一个错误,但是SFINAE原则并不会马上报告这个错误,而是等到如果真的没有别的可以满足可以被正确地实例化的模板的时候,才会报告这是一个错误!(这里有template<typename T>void FUN(...)模板,最终编译器选择了实例化这个模板,那个错误也就没有报告!)
SFINAE原则的特性:替换失败并非错误要视情况而定,即用户是否提供了可以满足被正确实例化的其他模板,如果没有则报错,如果有,则按下不表!
引用和限定符
一个例子引发出来的局限:
#include<iostream>
using namespace std;
template<typename T>
void apply(T & arg, void(*func)(T)){
func(arg);
}
void incr(int & a){
cout << "void incr(int & a)" << endl;
}
void print(int a){
cout << "void print(int a)" << endl;
}
void main(){
int x = 7;
apply(x, print);//工作良好,一切正常
//apply(x, incr);//error:有x推断出来T为int,但是void(*func)(int)和void incr(int &)无法匹配!
}
上述apply(x, incr)的问题在于:当用x推倒出模参之后,T为int,此时导致了void(*)(int)和void (*)(int &)的不匹配;
观察上面的例子,apply模板参数的特点在于:一个T基本类型,然后一个T&的类型,而我们遇到的问题在于(如果从函数参数着手来演绎)从int &只能推倒出int &&,而不能从当T为int &的时候,决定T &也是int &;因此我们可以利用一个能够解析基本类型、基本类型的引用的类来解决;
TypeOp头文件:
#ifndef TYPEOP
#define TYPEOP
//基本模板
template<typename T>
class TypeOp{
public:
typedef T ArgT;//当前类型
typedef T BareT;//基类型
typedef T const ConstT;//加了const的类型
typedef T & RefT;//加了引用的类型
typedef T & RefBareT;//基类型的引用类型
typedef T const & RefConstT;//加了const的引用类型
};
//const
template<typename T>
class TypeOp<T const>{
public:
typedef T const ArgT;//当前类型
typedef typename TypeOp<T>::BareT BareT;//基类型
typedef T const ConstT;//加了const的类型
typedef T const & RefT;//加了引用的类型
typedef typename TypeOp<T>::BareT & RefBareT;//基类型的引用类型
typedef T const & RefConstT;//加了const的引用类型
};
//&
template<typename T>
class TypeOp<T &>{
public:
typedef T & ArgT;//当前类型
//这里当T为int const &的时候,如何获得int——将int const再放到const的局部特化中萃取
typedef typename TypeOp<T>::BareT BareT;//基类型
typedef T const ConstT;//加了const的类型
typedef T & RefT;//加了引用的类型
//相同道理
typedef typename TypeOp<T>::BareT & RefBareT;//基类型的引用类型
typedef T const & RefConstT;//加了const的引用类型
};
//void特殊特化
template<>
class TypeOp<void>{
public:
typedef void ArgT;//当前类型
typedef void BareT;//基类型
typedef void const ConstT;//加了const的类型
//对于void,其引用家族都是void
typedef void RefT;//加了引用的类型
typedef void RefBareT;//基类型的引用类型
typedef void RefConstT;//加了const的引用类型
};
#endif
测试源文件:
#include<iostream>
#include"TypeOp.h"
using namespace std;
template<typename T>
void apply(typename TypeOp<T>::RefT arg, void(*func)(T)){
func(arg);
}
void incr(int & a){
cout << "void incr(int & a)" << endl;
}
void print(int a){
cout << "void print(int a)" << endl;
}
void main(){
int x = 7;
apply(x, print);//工作良好,一切正常
apply(x, incr);//工作良好,一切正常
}
此时因为TypeOp<T>::RefT 中T的演绎依赖于受限名称RefT,这是不可行的方法,详情可以看《Template parameter deduction(模板实参演绎)》,因此此时是通过后面的函数参数来推导出T的,然后再得出TypeOp<T>::RefT中RefT的类型!所以这样改变函数模板的定义之后,无论void(*func)(T)中的T是int还是int &,TypeOp<T>::RefT中的RefT都是int &!
promotion trait(类型提升trait)
一个问题引发的血案:
template<typename T1,typename T2>
vector< ? ? ? > AddVector(T1 & v1, T2 & v2);
对于上面的例子,我们想将两个vector的元素相加到另一个容器中并返回,但是我们想让返回的那个容器的元素类型是来源容器中元素类型较大的那个元素类型,但是这里显然我们无法优雅地解决!
解决方案:
IfThenElse头文件:
#ifndef IFTHENELSE
#define IFTHENELSE
//IfThenElse的作用:用来根据给定的条件抉择类型
template<bool C,typename Ta,typename Tb>
class IfThenElse;
template< typename Ta, typename Tb>
class IfThenElse<true, Ta, Tb>{
public:
typedef Ta ResultType;
};
template< typename Ta, typename Tb>
class IfThenElse<false, Ta, Tb>{
public:
typedef Tb ResultType;
};
#endif
promotion trait头文件:
#ifndef PROMOTION_TRAIT
#define PROMOTION_TRAIT
#include<vector>
#include"IfThenElse.h"
using namespace std;
//使用宏来进行特化
#define MK_PROMOTION(T1,T2,Tr) \
template<>class Promotion<T1,T2>{ \
public: \
typedef Tr ResultType; \
}; \
\
template<>class Promotion<T2, T1>{\
public: \
typedef Tr ResultType; \
}; \
//对于基本类型的promotion抉择
template<typename T1,typename T2>
class Promotion{
public:
typedef typename
IfThenElse<(sizeof(T1)>sizeof(T2)),
T1,
//类型相等——暂时void
typename IfThenElse<(sizeof(T1)<sizeof(T2)),T2,void>::ResultType
>::ResultType ResultType;
};
//对于vector容器的特化
template<typename T1,typename T2>
class Promotion<vector<T1>,vector<T2>>{
public:
typedef vector<typename Promotion<T1, T2>::ResultType> ResultType;
};
//相等类型特化
template<typename T>
class Promotion<T, T>{
public:
typedef T ResultType;
};
//相同类型容器的特化——这里必须写
//class Promotion<T, T>的特殊程度和class Promotion<vector<T1>,vector<T2>>的特殊程度一样
template<typename T>
class Promotion<vector<T>, vector<T>>{
public:
typedef vector<typename Promotion<T,T>::ResultType> ResultType;
};
//手动的指定的全局特化
MK_PROMOTION(bool, char, int);
MK_PROMOTION(int, long int, long int);
MK_PROMOTION(int, double, double);
#endif
测试源文件:
#include<iostream>
#include<vector>
#include"promotion trait.h"
using namespace std;
template<typename T1,typename T2>
vector<typename Promotion<T1, T2>::ResultType>
AddVector(vector<T1> & v1, vector<T2> & v2){
vector<typename Promotion<T1, T2>::ResultType> vr;
vr.push_back(v1[0]+v2[0]);
return vr;
}
void main(){
vector<int> v1{ 2 };
vector<double> v2{ 65535.1 };
auto v=AddVector(v1,v2);
cout << v.at(0)<< endl;
}
分析:Promotion模板可以对两个参数T1,T2返回一个提升之后的类型,当然这里的提升的意思是内存更大的类型作为返回的类型,这个类型更大的类型通过IfThenElse来产生!其中IfThenElse也特化了当两个类型相同的时候的情况,可以说,Promotion只是个外壳,其内部的功能提供是IfThenElse!其中也支持我们对Promotion的特化版本(即绕开了IfThenElse)!比如我们对于int和char可以将Promotion的返回类型特化为double也是可以的!其中用到了宏特化的知识!(这里面主要的内容是IfThenElse,其实可以不只是比较两个类型的大小作为IfThenElse的第一个模板参数,可以是别的其他条件,而这赋予了IfThenElse很广的作用范围!)
IFThenElse模板:
template< typename Ta, typename Tb>
class IfThenElse<true, Ta, Tb>{
public:
typedef Ta ResultType;
};
template< typename Ta, typename Tb>
class IfThenElse<false, Ta, Tb>{
public:
typedef Tb ResultType;
};
IfThenElse使用:
//对于基本类型的promotion抉择
template<typename T1,typename T2>
class Promotion{
public:
typedef typename
IfThenElse<(sizeof(T1)>sizeof(T2)),
T1,
//类型相等——暂时void
typename IfThenElse<(sizeof(T1)<sizeof(T2)),T2,void>::ResultType
>::ResultType ResultType;
};
policy trait
需求:在函数参数传递的过程中,根据参数的类型自动决定如何传递参数,是引用传递还是值传递,根据效率决定,例如有些类型虽然类型小,但是拷贝构造成本惊人(比如类),有些类型因为数据的庞大(如容器),必须传递引用,有些类型以值传递的成本比以引用传递的成本还小!因此需要工具根据类型自己的属性判断以什么方式传递!
IfThenElse头文件:
#ifndef IFTHENELSE
#define IFTHENELSE
//IfThenElse的作用:用来根据给定的条件抉择类型
template<bool C,typename Ta,typename Tb>
class IfThenElse;
//对于true的特化
template< typename Ta, typename Tb>
class IfThenElse<true, Ta, Tb>{
public:
typedef Ta ResultType;
};
//对于false的特化
template< typename Ta, typename Tb>
class IfThenElse<false, Ta, Tb>{
public:
typedef Tb ResultType;
};
#endif
IsClass头文件:
#ifndef ISCLASST
#define ISCLASST
template<typename T>
class IsClassT{
private:
typedef char One;
typedef struct{ char a[2]; } Two;
//test函数模板1
//如果是class类型的话,就会匹配这个模板函数,从0到指针的转换比匹配省略号更优先
template<typename C>
static One test(int C::*);
//test函数模板2
//只有真的不是类的时候,才会匹配这个省略号参数的函数
template<typename C>
static Two test(...);
public:
enum{ yes = sizeof(test<T>(0)) == 1 };
enum{ no = !yes };
};
#endif
rparam头文件:
#ifndef RPARAM
#define RPARAM
#include<vector>
#include<list>
#include"IfThenElse.h"
#include"IsClassT.h"
using namespace std;
//对于类类型,传递引用
template<typename T>
class RParam{
public:
typedef typename IfThenElse<
//判断是否是类类型
IsClassT<T>::no,
//如果不是类类型,又排除了容器等类型,如果是基本类型,将判断类型大小决定是否以引用传递
typename IfThenElse<(sizeof(T) <= sizeof(int)), T, T &>::ResultType,
//如果是类类型的话就引用
T &
>::ResultType Type;
};
//对于容器类型传递引用的特化
template<typename T>
class RParam<vector<T>>{
public:
typedef vector<T> & Type;
};
//对于容器类型传递引用的特化
template<typename T>
class RParam<list<T>>{
public:
typedef list<T> & Type;
};
#endif
测试源文件:
#include<iostream>
#include"rparam.h"
using namespace std;
//将根据模板参数的类型决定参数的传递方式
template<typename T1, typename T2>
void foo_core(typename RParam<T1>::Type p1, /*根据T1的类型决定参数传递类型*/
typename RParam<T2>::Type p2){
p1 += 10;
p2.a += 100;
}
//封装显式指定调用
template<typename T1,typename T2>
inline
void foo(T1 & t1,T2 & t2){
foo_core<T1, T2>(t1, t2);
}
//测试类
class MyClass{
public:
MyClass() :a(0){}
int a;
};
void main(){
char a = 'A';//小于sizeof(4)的类型
double b = 1.1;//大于sizeof(4)的类型
MyClass obj;//类类型
vector<int> v{ 2, 3 };//容器类型
cout << a << " " << obj.a << endl;
foo_core<char, MyClass>(a, obj);//a将以值的方式传递
cout << a << " " << obj.a << endl;
foo(a,obj);//封装测试
cout << a << " " << obj.a << endl;
foo(b, obj);//b将以引用的方式传递
cout << b << " " << obj.a << endl;
/*
输出结果:
A 0
A 100
A 200
11.1 300
*/
}
拷贝、交换、移动
CSM1头文件:
#ifndef CSM1
#define CSM1
#include<new>
#include<cassert>
#include<stddef.h>
#include"rparam.h"
using namespace std;
//基本模板
template<typename T, bool BitWise>
class BitOrClassCSM;
//用于对象安全拷贝对的局特化
template<typename T>
class BitOrClassCSM<T, false>{
public:
static void copy(typename RParam<T>::ResultType src, T * dst){
*dst = src;
}
static void copy_n(T const * src, T *dst, size_t n){
for (rsize_t k = 0; k < n; ++k){
dst[k] = src[k];
}
}
static void copy_init(typename RParam<T>::ResultType src, void * dst){
::new(dst)T(src);
}
static void copy_init_n(T const * src, void * dst, rsize_t n){
for (size_t k = 0; k < n; k++){
::new((void *)((char *)dst + k)) T(src[k]);
}
}
static void swap(T * a, T * b){
T tmp(*a);
*a = *b;
*b = tmp;
}
static void swap_n(T * a, T * b, size_t n){
for (size_t k = 0; k < n; k++){
T tmp(a[k]);
a[k] = b[k];
b[k] = tmp;
}
}
static void move(T * src, T * dst){
assert(src != dst);
*dst = *src;
src->~T();
}
static void move_n(T * src, T * dst, size_t n){
assert(src != dst);
for (size_t k = 0; k < n; k++){
dst[k] = src[k];
src[k].~T();
}
}
static void move_init(T * src, void * dst){
assert(src != dst);
::new(dst)T(*src);
src->~T();
}
static void move_init_n(T const * src, void * dst, size_t n){
assert(src != dst);
for (size_t k = 0; k < n; k++){
::new((void *)((char *)dst + k)) T(src[k]);
src[k].~T();
}
}
};
#endif
CSM2头文件:
#ifndef CSM2
#define CSM2
#include<string>
#include<cassert>
#include<stddef.h>
#include"CSM1.h"
using namespace std;
template<typename T>
class BitOrClassCSM<T, true>:public BitOrClassCSM<T, false>{
public:
static void copy_n(T const *src,T *dst,size_t n){
memcpy((void *)dst, (void *)src, n);
}
static void copy_init_n(T const *src,void *dst,size_t n){
memcpy(dst, (void *)src, n);
}
static void move_n(T * src,T * dst,size_t n){
assert(src != dst);
memcpy((void *dst, (void *)src, n);
}
static void move_init_n(T const * src, void * dst, size_t n){
assert(src != dst);
memcpy(dst, (void *)src, n);
}
};
#endif
总结:
本篇一共分析解决了这几个问题:
1)trait类表述了模板参数的一些自然的额外属性,policy表述了泛型函数和泛型类的一些可配置行为
2)对于类型函数的认识
3)判断一个类型是否是class——IfThenElse的实现(SFINAE原则)
4)引用和限定符——拆解出基本类型,然后配置出引用、const等组合类型
5)promotion类型提升——两个类型参数返回另一个类型参数(本篇实现的是返回两个类型较大的那个类型)
6)policy trait——函数传参过程中,通过参数的类型决定以什么方式传递
未完待续......