函数模板¶
1. 普通函数模板 (Function Template)¶
| C++ | |
|---|---|
1 2 3 4 | |
| C++ | |
|---|---|
1 2 3 4 5 | |
2. 可变参数模板 (Variadic Template)¶
class... Args 这种写法是 模板参数包,表示这里有一个数量可变的模板参数列表。
Args...是一个类型列表,比如(int, double, std::string)。- 和普通模板参数不同,它的数量可以是 0 个、1 个或多个。
- 对应的函数形参部分
Args&&... args就是函数参数包。
例子
| C++ | |
|---|---|
1 2 3 4 5 6 7 | |
3. 转发引用 (Forwarding Reference / Universal Reference)¶
在模板里,如果你写
| C++ | |
|---|---|
1 2 | |
并且 F / Args 是模板参数,&& 就不是单纯的右值引用,而是转发引用(C++11 引入)。
转发引用的特点:
- 既可以绑定到左值,也可以绑定到右值。
- 和
std::forward配合,可以保持实参的值类别(左值保持左值、右值保持右值)。 - 用于泛型代码中避免不必要的拷贝。
例子
| C++ | |
|---|---|
1 2 3 4 5 6 7 8 | |
在 enqueue 中:
| C++ | |
|---|---|
1 2 | |
这样设计是为了支持:
pool.enqueue(myFunction, 1, 2)pool.enqueue([]{ /* lambda */ })pool.enqueue(std::move(heavyObj))
并且尽可能减少拷贝。
4. 模板类型推导 (Template Argument Deduction)¶
F 和 Args... 的类型是在调用 enqueue 时由编译器推导出来的。
| C++ | |
|---|---|
1 | |
这里推导过程:
F→lambda的具体类型(匿名类型,只有编译器知道)。Args...→int。
5. 参数包展开 (Parameter Pack Expansion)¶
在函数体里经常会看到:
| C++ | |
|---|---|
1 | |
这里的 ... 是包展开运算符,它会把参数包 Args 和 args 按一一对应展开。
展开过程类似:
| C++ | |
|---|---|
1 | |
这也是为什么你能把不定个数的参数直接原封不动传给 std::bind 或 std::invoke。
6. 组合起来的意义¶
综合来看:
| C++ | |
|---|---|
1 | |
*
| C++ | |
|---|---|
1 | |
就是:
- 泛型:可支持任意可调用对象类型。
- 可变参数:支持任意数量和类型的参数传递给该可调用对象。
- 完美转发:保留参数的值类别(避免不必要的拷贝)。
- 高扩展性:可以支持普通函数、lambda、
std::function、仿函数等,且参数类型灵活。
如果你愿意,我可以帮你画一张调用时模板推导的流程图,把 F、Args... 在 enqueue 调用时是怎么一步步被推导出来的,以及为什么 && 会变成转发引用。
这个图会比文字更直观。
你想让我画吗?