本文介绍了如何将省略号 (
...
) 与 C++ 可变参数模板一起使用。 省略号在 C 和 C++ 中有多种用法。 其中包括函数的变量参数列表。 C 运行时库中的
printf()
函数是最著名的示例之一。
“可变参数模板”是支持任意数量的参数的类或函数模板。 此机制对 C++ 库开发人员特别有用:你可以将其应用于类模板和函数模板,从而提供广泛的类型安全和非微不足道的功能和灵活性。
可变参数模板以两种方式使用省略号。 在参数名称的左侧,表示“参数包”,在参数名称的右侧,将参数包扩展为单独的名称。
下面是
variadic 类模板
定义语法的基本示例:
template<typename... Arguments> class classname;
对于参数包和扩展,可以根据自己的偏好,在省略号周围添加空格,如以下示例所示:
template<typename ...Arguments> class classname;
或者此示例:
template<typename ... Arguments> class classname;
本文使用第一个示例中所示的约定, (省略号附加到 typename
) 。
在前面的示例中, Arguments
是一个参数包。 类 classname
可以接受可变数量的参数,如以下示例所示:
template<typename... Arguments> class vtclass;
vtclass< > vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;
vtclass<long, std::vector<int>, std::string> vtinstance4;
通过使用可变参数类模板定义,还可以要求至少一个参数:
template <typename First, typename... Rest> class classname;
下面是 可变函数模板 语法的基本示例:
template <typename... Arguments> returntype functionname(Arguments... args);
Arguments
然后扩展参数包以供使用,如下一部分所示。
可以采用其他形式的可变函数模板语法,包括但不限于以下示例:
template <typename... Arguments> returntype functionname(Arguments&... args);
template <typename... Arguments> returntype functionname(Arguments&&... args);
template <typename... Arguments> returntype functionname(Arguments*... args);
也允许使用说明符(如 const
):
template <typename... Arguments> returntype functionname(const Arguments&... args);
与可变参数模板类定义一样,可以生成要求至少一个参数的函数:
template <typename First, typename... Rest> returntype functionname(const First& first, const Rest&... args);
可变参数模板使用 sizeof...()
运算符(与旧的 sizeof()
运算符无关):
template<typename... Arguments>
void tfunc(const Arguments&... args)
constexpr auto numargs{ sizeof...(Arguments) };
X xobj[numargs]; // array of some previously defined type X
helper_func(xobj, args...);
有关省略号位置的更多信息
以前,本文介绍了以以下形式定义参数包和扩展的省略号放置:“在参数名称的左侧,它表示参数包,在参数名称的右侧,它将参数包展开为单独的名称”。 虽然从技术上讲是真的,但在代码转换中可能会造成混淆。 请注意以下几点:
在模板参数列表 (template <parameter-list>
) 中,typename...
引入了模板参数包。
在参数声明子句 (func(parameter-list)
) 中,“顶级”省略号引入了函数参数包,并且省略号定位很重要:
// v1 is NOT a function parameter pack:
template <typename... Types> void func1(std::vector<Types...> v1);
// v2 IS a function parameter pack:
template <typename... Types> void func2(std::vector<Types>... v2);
其中省略号显示在参数名称的后面,并且您具有一个参数包扩展。
说明可变函数模板机制的一个好方法是在重写 的一些功能 printf
时使用它:
#include <iostream>
using namespace std;
void print() {
cout << endl;
template <typename T> void print(const T& t) {
cout << t << endl;
template <typename First, typename... Rest> void print(const First& first, const Rest&... rest) {
cout << first << ", ";
print(rest...); // recursive call using pack expansion syntax
int main()
print(); // calls first overload, outputting only a newline
print(1); // calls second overload
// these call the third overload, the variadic template,
// which uses recursion as needed.
print(10, 20);
print(100, 200, 300);
print("first", 2, "third", 3.14159);
10, 20
100, 200, 300
first, 2, third, 3.14159
大多数包含可变函数模板的实现都使用某种形式的递归,但它与传统递归略有不同。 传统的递归涉及使用同一签名调用自身的函数。 (它可能会重载或模板化,但每次选择相同的签名。)可变参数递归涉及通过使用变化(基本都是减少)数量的参数来调用可变参数函数模板,从而每次都清除不同的签名。 仍然需要“基案例”,但递归的性质不同。