C++ primer->16.3- 高级篇
一、重载与模板
1、函数模板可以被另一个模板或一个普通非模板函数重载。这样,函数匹配规则会受到影响:
①:如果同样好的函数中只有一个是非模板函数,则选择此函数。
②:如果同样好的函数中没有非模板函数,而有多个函数模板,则选择其中更特例化的。
否则,调用歧义。
下面是一组重载的例子:
1 using std::string; 2 template<typename T> string debug_rep(const T& t) 3 { 4 std::ostringstream ret; 5 ret << t; 6 return ret.str(); 7 } 8 9 template<typename T> string debug_rep(T* p) 10 { 11 std::ostringstream ret; 12 ret << "pointer: " << p; 13 if (p) 14 ret << " " << debug_rep(*p); 15 else 16 ret << " null pointer"; 17 return ret.str(); 18 } 19 20 std::string debug_rep(const std::string& s) 21 { 22 return '"' + s + '"'; 23 } 24 25 std::string debug_rep(const char* p) 26 { 27 return debug_rep(string(p)); 28 }
二、可变参数模板
1、一个可变参数模板就是一个接受可变数目参数的模板函数或模板类。可变数目的参数被称为参数包。存在两种参数包:模板参数包,表示零个或多个模板参数;函数参数包,表示零个或多个函数参数。
我们用一个省略号来指出一个模板参数或函数参数表示一个包。在一个模板参数列表中,class...或typename...指出接下来的参数表示零个或多个类型的列表;一个类型后面跟一个省略号表示零个或多个给定类型的非类型参数的列表。在函数参数列表中,如果一个参数的类型是一个模板参数包,则此参数也是一个函数参数包。
template<typename T,typename... Args> void foo(const T&t, const Args&...rest);
2、当我们需要知道包中有多少元素时,可以使用sizeof...运算符。
3、下面是一个可变参数函数模板的例子,可变参数函数通常是递归的
template<typename T> std::ostream& print(std::ostream& os, const T& t) { return os << t; } template<typename T, typename... Args> std::ostream& print(std::ostream& os, const T& t, const Args&... rest) { os << t << ","; return print(os, rest...); }
4、包扩展
我们通过在模式右边放一个省略号(...)来触发扩展操作,上述例子的模式是const T&。对上述例子进行调用print(cout,i,s,42);
第一次调用时发生扩展,生成print(ostream&, const int&, const string&,const int&);
第二次扩展发生于类内调用调用print(os, rest...);等价于print(os,s,42);
5、转发参数包
在新标准下,我们可以组合使用可变参数模板与forward机制来编写函数,实现将其实参不变地传递给其他函数。
class StrVec { public: template <class... Args> void emplace_back(Args&&...) }
三、模板特例化
1、当我们不能(或不希望)模板版本时,可以定义类或函数模板的一个特例化版本。一个特例化版本就是模板的一个独立的定义,在其中一个或多个模板参数被指定为特定的类型。
template<typename T>int compare(const T&, const T&); //为了单独处理指针,我们应当指定一个特例化版本 template<> int compare(const char* const& p1, const char* const& p2) { return strcmp(p1,p2); }
2、函数重载与模板特例化
重要的请记住:一个特例化版本本质上是一个实例,而非函数名的一个重载版本。所以它的匹配机制在模板之中。
3、类模板特例化
1、除了特例化函数模板,我们还可以特例化类模板。
2、类模板部分特例化:与函数模板不同,类模板的特例化不必为所有模板参数提供实参。我们可以只指定一部分而非所有模板实参,或是参数的一部分而非全特效。部分特例化时,应当提供实参。
1 template <class T> 2 struct remove_reference 3 { 4 typedef T type; 5 } 6 7 template<class T> struct remove_reference<T&> 8 { 9 typedef T type; 10 } 11 12 template<class T> struct remove_reference<T&&> 13 { 14 typedef T type; 15 }