🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## [$]一.模板参数 ### 1.模板参数 与函数参数类似,模板参数分为模板形参和模板实参。 + 对于函数模板,可以不显式指定模板实参。但若编译器无法推断模板实参的类型,则需要指定模板实参。 + 对于类模板,必须指定模板实参。 ### 2.模板参数的作用域与可见性 1. 模板参数作用域开始:模板参数声明之后 2. 模板参数作用域结束:模板声明结束 3. 与任何其他名字一样,模板参数会隐藏外层作用域中声明的相同名字。 ```c++ template <typename T/*T作用域开始*/> T& max(const T& a,const T& b) { return a > b ? a : b; /*T作用域结束*/ } ``` ### 3.默认模板实参 1. 为类模板指定默认模板形参 ```c++ //声明 template <typename T=int> class Array; //定义对象,尖括号不能省 Array<> a; ``` 2. \[11+\]为函数模板指定默认模板形参 ```c++ template <typename T=int> T& max(const T&,const T&); ``` ### 4.模板实参为模板 ```c++ template <typename T> class Array {}; class Matrix { private: Array<Array<int>> arr; }; ``` >[warning]VC++6.0的BUG: 两个 `>` 之间要加一个空格,否则会被认为是运算符 ```c++ template <typename T> class Array {}; class Matrix { private: Array<Array<int> > arr; }; ``` ### [$]5.非类型模板参数(VC++6.0不支持) 1. 一个非类型模板参数表示一个值,而不是一个类型。 2. 非类型模板参数通常用于含有大小的复合类型,如指向数组的指针,数组的引用。 3. 非类型模板参数的模板实参必须是 **常量表达式** ,以保证编译器正常实例化。 > VC++6.0不可运行下面案例 ```c++ template <unsigned N1, unsigned N2> int compare(const char (&p1)[N1], const char (&p2)[N2]) { return strcmp(p1, p2); } ``` ### [$]6.模板参数与作用域运算符 + 默认情况下,C++语言假定通过作用域运算符访问的名字不是类型。 ```c++ template <typename T> T::type top() //T是一个类型名,而不是一个模板参数 { //code } ``` + 使用 `typename` 关键字指定通过作用域运算符访问的名字含有模板参数: ```c++ template <typename T> typename T::type top() //T是一个类型名,而不是一个模板参数 { //code } ``` ## \[11+\][$]二.可变参数模板 ### 1.基本概念 1. **可变参数模板** 接受可变数目参数的模板函数或模板类。 2. **参数包** 可变数目的参数,只能获取大小或拓展,不能直接求值或调用。存在两种参数包: + 模板参数包:表示零个或多个模板参数 + 函数参数包:表示零个或多个函数参数 3. **拓展** 扩展一个包就是将它分解为构成的元素,对每个元素应用 **模式** ,获得扩展后的列表。在模式右边放一个省略号来触发扩展操作。 4. **模式** 对参数包进行的处理。 ### 2.声明一个可变参数模板 在模板参数名前添加省略号,表示模板参数包。在函数参数名前添加省略号,表示函数参数包。 ```c++ //Args是模板参数包 //expr是函数参数包 template <typename... args> void print(const args &... expr) { } ``` ### 3.sizeof...运算符 用于获取包中的元素个数。不会对表达式求值,返回一个常量表达式。 #### 例1 ```c++ template <typename... args> void count(const args &... rest) { cout << "模板参数包的元素个数:" << sizeof...(args) << endl; cout << "函数参数包的元素个数:" << sizeof...(rest) << endl; } ``` 运行下面语句: ```c++ count(2, "4", 3.0, "8"); ``` >[test] >模板参数包的元素个数:4 >函数参数包的元素个数:4 ### 4.获取参数包的内容 1. 可变参数函数通常是递归的,需要额外定义一个非可变参数的函数。在函数匹配时,非可变参数的函数优先匹配。 #### 例2 为了终止递归,需要先定义一个非可变参数的函数: ```c++ template <typename T> void print(const T &one) { cout << one << endl; } ``` 再来定义含参数包的函数: ```c++ template <typename T, typename... args> void print(const T &one, const args &... rest)//拓展args:模式为 const args & ,生成一个参数列表 { cout << one << endl; return print(rest...);//拓展rest:模式为 rest ,其中第一个参数会传递给 one,其余参数传递给 rest } ``` 运行下面语句: ```c++ print(2, "4", 3.0, "888"); ``` >[test] >2 >4 >3 >888 运行过程中的调用顺序: | # | 函数 | 参数 `one` | 参数包 `rest` | |----|----|----|----| | 1|`print(2, "4", 3.0, "888")`|`2`|`"4", 3.0, "888"`| | 2 |`print("4", 3.0, "888")`|`"4"`|`3.0, "888"`| | 3|`print(3.0, "888")`|`3.0`|`"888"`| | 4|`print("888")`|`"888"`|空| #### 例3 假设有以下定义: ```c++ template <typename T> void print(const T &one) { cout << one << endl; } template <typename T, typename... args> void print(const T &one, const args &... rest) //拓展args:模式为 const args & ,生成一个参数列表 { cout << one << endl; return print(rest...); //拓展rest:模式为 rest ,其中第一个参数会传递给 one,其余参数传递给参数包 rest } //针对字符串的格式化,加上引号 const string format(const char *one) { return string("\"") + one + string("\""); } //针对其他类型的格式化 template <typename T> const T &format(const T &one) { return one; } template <typename... args> void format_print(const args &... exp) { print(format(exp)...);//拓展exp:拓展后每个参数均为 format(exp) } ``` 运行下面语句: ```c++ format_print(2, "4", 3.0, "888"); ``` >[test] >2 >"4" >3 >"888"