条款55模板的模板参数
见一下stack适配器采用默认Deque的例子
template<typename T, class Cont =Deque<T>>
class Stack{
public:
~stack();
void push();
private:
Conts_;
};
这里,Stack的用户现在必须提供一个模板实参,表示元素的类型,还可以提供一个表示容器的类型(默认为deque<T>),并且容器必须能够容纳该元素类型的对象。
如果Stack的用户乐于接受一个Deque实现或者不是特别关心其实现时,这回很有帮助:
Stack<int>aStack1; //容器Deque<int>
Stack<double>aStack2; //容器是Deuque<double>
然而,这种方式是以安全性为代价的,因为当其使用其他容器进行特化时,这种方式仍然需要协调元素和容器的类型,显然,这种协调的要求,就可能会带来不协调:
Stack<int,List<int>> aStack3;
Stack<int,List<unsigned>> aStack4; //哎呀!错误!
解决办法:一个模板可以带有一个自身就是模板名字的参数。这种参数叫做:模板的模板参数。
Stack模板使用其类型名字参数来实例化其模板的模板参数,所得到的容器类型用于实现Stack:
template<typenameT, template <typename> class Cont = Deque>
classStack{
//…
private:
Cont<T>s_;
};
这种方式可以使元素和容器类型之间的协调问题通过Stack自身的实现来解决,而不是特化Stack的各种代码中进行解决。
Stack<int,List> aStack1; //OK,Cont是一个List
Stack<std::string> aStack; //OK,使用默认参数值,Cont是一个Deque
条款56 policy
如果一个策略比其他策略更常用,通常可把它作为默认policy。
条款57模板实参推导
template<typename T>
T min(const T& a, const T& b)
{return a<b ?a:b; }
int a = min(12,13); //T是int
double d = min(‘\b’, ‘\a’); //T是char
char c = min(12.3, 4); //错误!T不能既是double又是int
上面代码是错误的,原因在于编译器无法在默认两个的情况下推导出模板参数。在这种情况下,我们总是可以用显式的方式告诉编译器,模板实参究竟是什么:
d = min<double>(12.3, 4); //OK,T是double
让编译器根据函数调用中的实参类型推导模板实参。这在情理中,这个过程称为实参推导。任何无法从实参推导出来的模板实参都必须显式予以提供。
函数模板实参推导机制可用来间接地特化类模板。考虑一个可用于从函数指针来生成函数对象的类模板:
template<typenameA1, typename A2, typename R>
class Pfun2 :public std::binary_function<A1,A2,R>{
public:
explict Pfun2(R (*fp)(A1, A2)) :fp_(fp){}
R operator()(A1 a1, A2 a2) const{ returnfp_(a1, 2);}
private:
R (*fp_)(A1, A2);
};
直接实例化这个模板有点麻烦:
boolisGreater(int, int);
std::sort(b, e, Pfun2<int,int, bool>(isGreater)); //痛苦!
对于此种情况,通常可以提供一个辅助函数,该函数唯一用途在于推导模板实参,为的是可以像施魔法一般去特化一个类模板:
template<typenameR, typename A1, typename A2>
inline Pfun2<A1,A2,R>makePFun(R(*pf)(A1, A2))
{return Pfun2<A1,A2,R>(PF);}
//…
std::sort(b,e,makePFun(isGreater)); //好多了…
条款58 重载函数模板
一个函数可以被其他函数模板和非模板函数重载。
条款59 SFINAE
SFINAE(substition failure is neo an error)替换失败并非错误