可变参数模板
基本语法及原理
C++11 支持可变参数模板,即支持可变数量参数的函数模板和类模板。可变数目的参数称为参数包,存在两种参数包:模板参数包(表示零或多个模板参数)和函数参数包(表示零或多个函数参数)。
C++11 可变参数模板支持零个或多个参数,通过参数包和省略号实现泛型编程的数量变化。包扩展分为递归方式和函数参数展开方式,编译器在编译期完成实例化。emplace 系列接口允许在容器内原地构造对象,提升性能。新增类功能包括默认移动语义、成员变量默认值、default/delete 控制特殊成员函数以及 final/override 控制继承行为。包装器包含 function 和 bind,用于封装可调用对象。

C++11 支持可变参数模板,即支持可变数量参数的函数模板和类模板。可变数目的参数称为参数包,存在两种参数包:模板参数包(表示零或多个模板参数)和函数参数包(表示零或多个函数参数)。
template <class ...Args> void Func(Args&&... args) {}
使用省略号来指出一个模板参数或函数参数表示一个包。在模板参数列表中,class... 或 typename... 指出接下来的参数表示零或多个类型列表;在函数参数列表中,类型名后面跟 ... 指出接下来表示零或多个形参对象列表。函数参数包可以用左值引用或右值引用表示,遵循引用折叠规则。
可变参数模板的原理本质还是去实例化对应类型和个数的多个函数。可以使用 sizeof... 运算符计算参数包中参数的个数。
template <class ...Args> void Print(Args&&... args) { cout << sizeof...(args) << endl; }
int main() {
double x = 2.2;
Print(); // 包里有 0 个参数
Print(1); // 包里有 1 个参数
Print(1, string("xxxxx")); // 包里有 2 个参数
Print(1.1, string("xxxxx"), x); // 包里有 3 个参数
return 0;
}
编译本质会结合引用折叠规则实例化出对应的函数。有了可变参数模板,我们在类型泛化基础上叠加数量变化,让泛型编程更灵活。
对于参数包,除了计算参数个数,主要操作是扩展它。扩展一个包就是将它分解为构成的元素,对每个元素应用模式,获得扩展后的列表。通过在模式的右边放一个省略号 (...) 来触发扩展操作。 C++ 还支持更复杂的包扩展,直接将参数包依次展开作为实参给一个函数处理。 不支持用下面的方式去进行包扩展(运行时无法获取解析):
template <class ...Args> void Print(Args... args) {
cout << sizeof...(args) << endl;
for (size_t i = 0; i < sizeof...(args); i++) {
cout << args[i] << " ";
}
cout << endl;
}
下面展示使用递归的一种包扩展方式。
void ShowList() {
cout << endl;
}
template <class T, class ...Args> void ShowList(T x, Args... args) {
cout << x << " ";
ShowList(args...);
}
template <class ...Args> void Print(Args... args) {
ShowList(args...);
}
int main() {
Print(1, string("xxxxx"), 2.2);
return 0;
}
编译器将可变参数模板通过模式的包扩展,推导出具体的重载函数。
template <class T> const T & GetArg(const T & x) {
cout << x << " ";
return x;
}
template <class ...Args> void Arguments(Args... args) { }
template <class ...Args> void Print(Args... args) {
Arguments(GetArg(args)...);
}
int main() {
Print(1, string("xxxxx"), 2.2);
return 0;
}
编译器在编译时将函数模板扩展实例化为具体的函数调用。
C++11 以后 STL 容器新增了 emplace 系列的接口,如 emplace_back 和 emplace。这些接口均为模板可变参数,功能上兼容 push 和 insert 系列,但是 emplace 还支持直接插入构造对象的参数,这样有些场景会更高效一些,可实现原地构造,避免临时对象拷贝。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online