# 一、可变参数模板
#CPP11
# 1. 概述
C++ 的可变参数模板(Variadic Templates)是一种强大的特性,使得模板能够接受可变数量的参数。这一特性引入于 C++11,允许程序员创建更加灵活和通用的代码。
可变参数模板使得函数模板和类模板能够接受任意数量的模板参数,无论是类型参数还是非类型参数。它们为泛型编程提供了更大的灵活性。
# 2. 基本语法
可以通过使用省略号 ...
来定义参数包。
函数模板:
1
2
3
4template<typename... Args>
void func(Args... args) {
// 处理参数
}类模板:同理。
1
2
3
4
5
6
7template<typename... Args>
class MyClass {
public:
MyClass(Args... args) {
// 构造函数处理
}
};
# 3. 参数展开
展开参数包(pack expansion)是将模板参数包展开成单个参数序列。
在函数模板或类模板中,可以使用参数包展开来处理传入的参数。展开的方式通常使用以下语法:
- 在函数参数中展开:直接使用
args...
。(见下文 递归模板 的举例) - 在函数体中展开:使用
(..., expression)
的形式。(即,使用折叠表达式进行展开)
1 | template<typename... Args> |
# 4. 示例
以下是一个简单的示例,演示如何使用模板参数包:
1 |
|
# 5. 递归模板
可以通过递归模板实现对参数包的处理。例如,计算参数包中所有数值的和:
1 | template<typename T> |
# 6. 使用非类型模板参数
除了类型参数,模板参数包也可以使用非类型参数:
1 | template<int... Values> |
# 7. 结合其他特性
模板参数包可以与其他 C++ 特性结合使用,如 SFINAE(Substitution Failure Is Not An Error)和类型推导,使得编写更加复杂的泛型代码变得可能。
# 二、折叠表达式
#CPP17
# 1. 基本概念
折叠表达式(Fold Expression)是 C++17 引入的一种特性,用于简化对可变参数模板的操作,尤其是在处理参数包时。它允许你对参数包中的元素进行简单的折叠运算(例如加法、乘法、逻辑运算等),减少了编写递归模板的需要。
折叠表达式是通过指定一个二元操作符(如 +
、 *
、 &&
等),对参数包中的所有元素进行运算的简洁方式。
# 2. 语法
折叠表达式有四种主要形式:
- 一元右折叠(Unary Right Fold):
( ... op pack )
- 一元左折叠(Unary Left Fold):
( pack op ... )
- 二元右折叠(Binary Right Fold):
( init op ... op pack )
- 二元左折叠(Binary Left Fold):
( pack op ... op init )
具体地,有:
一元右折叠:
1
2
3
4
5
6
7
8template<typename... Args>
auto sum(Args... args) {
return (args + ...); // 等价于 (((args1 + args2) + args3) + ...)
}
int main() {
std::cout << sum(1, 2, 3, 4); // 输出: 10
}一元左折叠:
1
2
3
4
5
6
7
8template<typename... Args>
auto sum(Args... args) {
return (... + args); // 等价于 (... + (args1 + (args2 + (args3 + ...))))
}
int main() {
std::cout << sum(1, 2, 3, 4); // 输出: 10
}二元右折叠:
1
2
3
4
5
6
7
8template<typename... Args>
auto sum(Args... args) {
return (0 + ... + args); // 初始化值为0,等价于 (0 + ((args1 + args2) + args3) + ...)
}
int main() {
std::cout << sum(1, 2, 3, 4); // 输出: 10
}二元左折叠:
1
2
3
4
5
6
7
8template<typename... Args>
auto sum(Args... args) {
return (args + ... + 0); // 初始化值为0,等价于 (... + (args1 + (args2 + (args3 + ... + 0))))
}
int main() {
std::cout << sum(1, 2, 3, 4); // 输出: 10
}
# 三、示例代码与解析
结合模板参数包和折叠表达式,我们可以编写出非常简洁和强大的模板函数。
例如,一个同时支持不同数据类型的打印函数:
1 |
|
这里我们逐行解释上面的模板函数 print,并详细说明模板参数包和折叠表达式的工作原理。
模板声明:
1
template<typename... Args>
这一行代码声明了一个模板,其中Args
是一个模板参数包。typename... Args
表示Args
可以接受任意数量的模板参数。函数定义:
1
void print(Args... args) {
这一行定义了一个名为 print
的函数。 Args... args
表示 args
是一个函数参数包,对应于模板参数包 Args
。每个 Args
中的类型对应于一个 args
中的参数。
- 折叠表达式:
1
(std::cout << ... << args) << '\n';
这一行是函数体,使用了折叠表达式来输出所有参数,并在末尾输出一个换行符。