模板与泛型编程——重载与模板,可变函数模板,模板特例化

 

 

1、重载与模板ios

  函数模板能够被另外一个模板或普通非模板函数重载。与往常同样,名字相同的函数必须具备不一样数量或类型的参数。数组

  若是涉及函数模板,则函数匹配规则会在如下几方面受到影响:ide

  • 对于一个调用,其候选函数包括全部模板实参推断成功的函数模板实例。
  • 候选的函数模板老是可行的,由于模板实参推断会排除任何不可行的模板。
  • 与往常同样,可行函数(模板与非模板)按类型转换(若是对此调用须要的话)来排序。固然,能够用于函数模板调用的类型转换是很是有限的。
  • 与往常同样,若是恰有一个函数提供比任何其余函数都更好的匹配,则选择此函数。可是,若是有多个函数提供一样好的匹配,则:——若是一样好的函数中只有一个是非模板函数,则选择此函数。——若是一样好的函数中没有非模板函数,而有多个函数模板,且其中一个模板比其余模板更特例化,则选择此模板。——不然,调用有歧义。

 

2、可变函数模板函数

  一个可变参数模板就是一个接受可变数目参数的模板函数或模板类。可变数目的参数被称为参数包。存在两种参数包:模板参数包,表示零个或多个模板参数;函数参数包,表示零个或多个函数参数。spa

  咱们用一个省略号来指出一个模板参数或函数参数表示一个包。在一个模板参数列表中,class...或typename ...指出接下来的参数表示零个或多个类型的列表;一个类型名后面跟一个省略号表示零个或多个给定类型的非类型参数的列表。在函数参数列表中,若是一个参数的类型是一个模板参数包,则此参数包也是一个函数参数包。debug

  template<typename T, typename...args>
  std::ostream &print(std::ostream &os, const T &t, const args &...rest);指针

args是一个模板参数包;rest是一个函数参数包。args表示零个或多个模板类型参数,rest表示零个或多个函数参数。rest

1)sizeof...运算符code

  当咱们须要知道包中有多少元素时,可使用sizeof...运算符。sizeof...返回一个常量表达式,并且不会对其实参求值。对象

  template<typename T, typename...args>
  std::ostream &print(std::ostream &os, const T &t, const args &...rest) {
    std::cout << sizeof...(args) << std::endl; // 类型参数的数目
    std::cout << sizeof...(rest) << std::endl; // 函数参数的数目
  }

 

一、可变参数函数模板

  当咱们既不知道想要处理的实参的数目也不知道它们的类型时,可变参数函数是颇有用的。

  可变参数函数一般是递归的。第一步调用处理包中的第一个实参,而后用剩余实参调用自身。咱们自定义的print函数也是这样的模式,每次递归调用将第二个实参打印到第一个实参表示的流中。为了终止递归,咱们还须要定义一个非可变参数的print函数,它接受一个流和一个对象。

 1 #include <iostream>
 2 #include <memory>
 3 #include <string>
 4 #include <vector>
 5 
 6 template<typename T>
 7 std::ostream &print(std::ostream &os, const T &t) {  8     return os << t;  9 } 10 
11 template<typename T, typename...args>
12 std::ostream &print(std::ostream &os, const T &t, const args &...rest) { 13     os << t << ", "; 14     return print(os, rest...); // 递归调用,打印其余实参
15 } 16 int main() 17 { 18     print(std::cout, 2, 3.3, "hello"); 19     return 0; 20 }
View Code

 

二、包扩展

  对于一个参数包,除了获取其大小外,咱们能对它作的惟一的事情就是扩展它。当扩展一个包时,咱们还要提供用于每一个扩展元素的模式。扩展一个包就是将它分解为构造的元素,对每一个元素应用模式,得到扩展后的列表。咱们经过在模式右边放一个省略号(...)来触发扩展操做。

1 template<typename T, typename...args>
2 std::ostream &print(std::ostream &os, const T &t, const args &...rest) { 3     os << t << ", "; 4     return print(os, rest...); // 递归调用,打印其余实参
5 }
View Code

  对args的扩展中,编译器将模式const args&应用到模板参数包args中的每一个元素。所以,此模式的扩展结果是一个逗号分隔的零个或多个类型的列表,每一个类型都形如const type&。

  第二个扩展发生在对print的递归调用中。在此状况下,模式是函数参数包的名字(即rest)。此模式扩展出一个由包中元素组成的、逗号分隔的列表。

1)理解包扩展

  C++语言还容许更复杂的扩展模式。例如,咱们能够编写第二个可变参数,对其每一个实参调用debug_rep,而后调用print打印string。

 1 #include <iostream>
 2 #include <sstream>
 3 #include <memory>
 4 #include <string>
 5 #include <vector>
 6 
 7 template<typename T>
 8 std::ostream &print(std::ostream &os, const T &t) {  9     return os << t; 10 } 11 
12 template<typename T, typename...args>
13 std::ostream &print(std::ostream &os, const T &t, const args &...rest) { 14     os << t << ", "; 15     return print(os, rest...); // 递归调用,打印其余实参
16 } 17 
18 template<typename T>
19 std::string debug_rep(const T &t) { 20  std::ostringstream ret; 21     ret << "-" << t << "-"; 22     return ret.str(); 23 } 24 
25 template<typename... args>
26 std::ostream &errorMsg(std::ostream &os, const args& ...rest) { 27     return print(os, debug_rep(rest)...); 28 } 29 int main() 30 { 31     errorMsg(std::cout, 2, 3.3, "hello"); 32     return 0; 33 }
View Code

 

三、转发参数包

 

3、模板特例化

  编写单一模板,使之对任何可能的模板实参都是最合适的,都能实例化,这并不老是能办到。在某些状况下,通用模板的定义对特定类型是不合适的:通用定义可能编译失败或作得不正确。其余时候,咱们也能够利用某些特定知识来编写更高效的代码,而不是从通用模板实例化。当咱们不能(或不但愿)使用模板版本时,能够定义类或函数模板的一个特例化版本。

1)定义函数模板特例化

  当咱们特例化一个函数模板时,必须为原模板中的每一个模板参数都提供实参。为了指出咱们正在实例化一个模板,应使用关键字template后跟一个空尖括号对(<>)。空尖括号指出咱们将为原模板的全部模板参数提供实参。

1 template<>
2 void show(const char* const &x) { // show的特殊版本,处理字符数组的指针
3     std::cout << x << " second" << std::endl; 4 }
View Code

2)函数重载与模板实例化

  当定义函数模板的特例化版本时,咱们本质上接管了编译器的工做。即,咱们为原模板的一个特殊实例提供了定义。重要的是要弄清:一个特例化版本本质上是一个实例,而非函数名的一个重载版本

  咱们将一个函数定义为一个特例化版本仍是一个独立的非模板函数,会影响到函数匹配。

  注意:模板及其特例化版本应该声明在同一个头文件中。全部同名版本的声明应该放在前面,而后是这些模板的特例化版本。

  将函数定义为特例化版本:

 1 #include <iostream>
 2 #include <sstream>
 3 #include <memory>
 4 #include <string>
 5 #include <vector>
 6 
 7 template<typename T>
 8 void show(const T& x) {  9     std::cout << x << " first" << std::endl; 10 } 11 
12 template<>
13 void show(const char* const &x) { // show的特殊版本,处理字符数组的指针
14     std::cout << x << " second" << std::endl; 15 } 16 
17 template<std::size_t N>
18 void show(const char(&x)[N]) { 19     std::cout << x << " third" << std::endl; 20 } 21 int main() 22 { 23     show("hi"); 24     return 0; 25 }
View Code

  将函数定义为普通函数:

 1 #include <iostream>
 2 #include <sstream>
 3 #include <memory>
 4 #include <string>
 5 #include <vector>
 6 
 7 template<typename T>
 8 void show(const T& x) {  9     std::cout << x << " first" << std::endl; 10 } 11 
12 void show(const char* const &x) { 13     std::cout << x << " second" << std::endl; 14 } 15 
16 template<std::size_t N>
17 void show(const char(&x)[N]) { 18     std::cout << x << " third" << std::endl; 19 } 20 int main() 21 { 22     show("hi"); 23     return 0; 24 }
View Code

3)类模板特例化

  咱们还能够特例化类模板。

 1 #include <iostream>
 2 #include <sstream>
 3 #include <memory>
 4 #include <string>
 5 #include <vector>
 6 
 7 template<typename T>
 8 class Base {  9 public: 10  Base(T x):data(x){} 11     void show() { 12         std::cout << "T" << std::endl; 13  } 14 private: 15  T data; 16 }; 17 // Base的特例化版本
18 template<>
19 class Base<int> { 20 public: 21     Base(int x) :data(x) {} 22     void show() { 23         std::cout << "int" << std::endl; 24  } 25 private: 26     int data; 27 }; 28 int main() 29 { 30     Base<std::string> sb("hi"); 31  sb.show(); 32     Base<int> ib(233); 33  ib.show(); 34     return 0; 35 }
View Code

4)类模板部分特例化

  与函数模板不一样,类模板的特例化没必要为全部模板参数提供实参。咱们能够指定一部分而非全部模板参数,或是参数的一部分而非所有特性。一个类模板的部分特例化自己是一个模板,使用它时用户还必须为那些在特例化版本中未指定的模板参数提供实参。

  咱们只能够部分特例化类模板,而不能部分特例化函数模板。

  因为一个部分特例化版本本质是一个模板,与往常同样,咱们首先定义模板参数。相似任何其余特例版本,部分特例化版本的名字与原模板的名字相同。对每一个未彻底肯定类型的模板参数,在特例化版本的模板参数列表中都有一项与之相应。在类名以后,咱们要为特例化的模板参数指定实参,这些实参列于模板名以后的尖括号中。这些实参与原始模板中的参数按位置对应。

5)特例化成员

  咱们能够只特例化成员函数而不是特例化整个模板。

 1 #include <iostream>
 2 #include <sstream>
 3 #include <memory>
 4 #include <string>
 5 #include <vector>
 6 
 7 template<typename T>
 8 class Base {  9 public: 10  Base(T x):data(x){} 11     void show() { 12         std::cout << "T" << std::endl; 13  } 14 private: 15  T data; 16 }; 17 
18 template<>
19 void Base<int>::show() { 20     std::cout << "int" << std::endl; 21 } 22 int main() 23 { 24     Base<std::string> sb("hi"); 25  sb.show(); 26     Base<int> ib(233); 27  ib.show(); 28     return 0; 29 }
View Code

相关文章
相关标签/搜索