本文介绍了如何将省略号 ( ... ) 与 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
    

    大多数包含可变函数模板的实现都使用某种形式的递归,但它与传统递归略有不同。 传统的递归涉及使用同一签名调用自身的函数。 (它可能会重载或模板化,但每次选择相同的签名。)可变参数递归涉及通过使用变化(基本都是减少)数量的参数来调用可变参数函数模板,从而每次都清除不同的签名。 仍然需要“基案例”,但递归的性质不同。

  •