## [$]一.模板参数
### 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"
- 阅读说明
- 1.1 概述
- C++基础
- 1.2 变量与常量
- 1.2.1 变量
- 1.2.2 字面值常量
- 字符型常量
- 数值型常量
- 1.2.3 cv限定符
- 1.3 作用域
- 1.3.1 标识符
- 1.3.2 *命名空间
- 1.3.3 作用域
- 1.3.4 可见性
- 1.4 数据类型
- 1.4.1 概述
- 1.4.2 处理类型
- 类型别名
- * auto说明符
- * decltype说明符
- 1.4.3 数组
- 1.4.4 指针
- 1.4.5 引用
- 1.5 表达式
- 1.5.1 概述
- 1.5.2 值的类别
- 1.5.3 *初始化
- 1.5.4 运算符
- 算术运算符
- 逻辑和关系运算符
- 赋值运算符
- 递增递减运算符
- 成员访问运算符
- 位运算符
- 其他运算符
- 1.5.5 *常量表达式
- 1.5.6 类型转换
- 第2章 面向过程编程
- 2.1 流程语句
- 2.1.1 条件语句
- 2.1.2 循环语句
- 2.1.3 跳转语句
- 2.1.4 *异常处理
- 2.2 函数
- 2.2.1 概述
- 2.2.2 函数参数
- 2.2.3 内置函数
- 2.2.4 函数重载
- 2.2.5 * 匿名函数
- 2.3 存储和生命期
- 2.3.1 生命周期与存储区域
- 2.3.2 动态内存
- 2.4 *预处理命令
- 第3章 面向对象编程
- 3.1 概述
- 3.2 类和对象
- 3.3 成员
- 3.3.1 访问限制
- 3.3.2 常成员
- 3.3.3 静态成员
- 3.3.4 成员指针
- 3.3.5 this指针
- 3.4 特殊的成员函数
- 3.4.1 概述
- 3.4.2 构造函数
- 3.4.3 析构函数
- 3.4.4 拷贝语义
- 3.4.5 * 移动语义
- 3.5 友元
- 3.6 运算符重载与类型转换
- 3.6.1 概述
- 3.6.2 重载方法
- 3.6.3 类型转换
- 3.7 继承与多态性
- 3.7.1 概述
- 3.7.2 派生类
- 3.7.3 子类型
- 3.7.4 虚基类
- 3.7.5 虚函数
- 3.7.6 抽象类
- 3.8 模板与泛型
- 3.8.1 概述
- 3.8.2 模板类型
- 3.8.3 *模板参数
- 3.8.4 *模板编译
- 3.8.5 *模板推断
- 3.8.6 *实例化与特例化
- 第4章 C++标准库
- 4.1 概述
- 4.2 输入输出流
- 4.2.1 概述
- 4.2.2 *流的状态
- 4.2.3 *常用流
- 4.2.4 *格式化I/O
- 4.2.5 *低级I/O
- 4.2.6 *随机访问
- 4.3 *C输入输出
- 4.3.1 *字符输入输出
- 4.3.2 *格式化输入输出
- 4.4 * 容器
- 4.4.1 * 概述
- 4.4.2 * 基本操作
- 4.4.3 * 顺序容器
- 4.4.4 * 迭代器
- 4.4.5 * 容器适配器
- 4.5 * 泛型算法
- 4.6 * 内存管理
- 4.6.1 * 自动指针
- 4.7 * 其他设施