模板参数包中的6个点是什么?

时间:2014-12-21 23:06:40

标签: c++ c++11 c++14 variadic-templates partial-specialization

在查看this问题的同时,我发现自己身处cpp reference site,在那里我发现了一种奇怪的,对我来说很新的语法:

template<class Ret, class... Args>
struct is_function<Ret(Args......)volatile &&> : std::true_type {};
是的,6点!最初我认为这是一个拼写错误,但在再次检查了libstdc ++ source后,就在第444行:

template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) volatile &&> : public true_type { };

这是一种有效的语法吗?点点,用于打包和解包参数包? 6个点做什么?

2 个答案:

答案 0 :(得分:56)

为什么libstdc++... ...的实施中使用is_function?如果我们查看std::is_function的cppreference部分,它会提供一个示例实现并说明第一个... ...案例:

// specialization for variadic functions such as std::printf
template<class Ret, class... Args>
struct is_function<Ret(Args......)> : std::true_type {};

所以我们需要第二组...来匹配像printf这样的可变函数:

           Comma optional as per 8.3.5 [dcl.fct] 
           |
           v
Ret(Args... ...)
        ^   ^
        |   |
        Match a function with a variable number of arguments
            |
            and the function is a variadic function

注意,我们有像fprintf这样的函数,它们在可变参数项之前有两个参数,我们也需要匹配它们。 实际上,如果我们使用该实现并尝试在没有printf专门化的情况下匹配... ...,那么它将失败see it live

此帖C++11's six dots中包含了该语言的一角:

  

前几天我正在捣乱,发现这个奇怪的小怪:

template <typename... Args>
void foo(Args......);
     

事实证明,......可以完全有效的C ++ 11。当向后兼容性与新的热度混合时会发生这种情况。

     

//这些都是等价的。

template <typename... Args> void foo1(Args......);
template <typename... Args> void foo2(Args... ...);
template <typename... Args> void foo3(Args..., ...);
     

希望最后一个显示这里发生的事情。 [...]

为什么这个有效?我们可以看到, ...与草案C ++ 11标准部分... [dcl.fct] 中的8.3.5同义,后者具有以下语法:< / p>

parameter-declaration-clause:
  parameter-declaration-listopt...opt
  parameter-declaration-list , ...

并说:

  

[...]语法正确,“......”不在其中   一个抽象声明符,“,......”与“......”同义。 [...]

答案 1 :(得分:8)

在这种情况下,两者用于不同的目的。第一个用于参数包扩展,第二个用于可变参数列表。该特定声明是处理带有一些常规参数和可变参数列表的函数。

差异在于运行时和编译时的可变性。在运行时获取可变数量参数的函数是特殊的。它是一个单一的函数,可以处理来自调用者的可变数量的参数:

void f(int x,...) // equivalent to void f(int x ...)
{
    // Do some run-time logic here to determine what to
    // do with parameters after x.
}

这与我们希望能够拥有一个模板的概念截然不同,该模板使用各种函数以及编译时已知的各种参数。例如,我们可以定义一个函数模板,它接受一个指向函数的指针,并允许参数的数量和类型变化:

template <typename... Args>
void g(void (*function_ptr)(Args...)) 
{ 
    // We can do logic here to call function_ptr with the proper
    // number of arguments.
}

鉴于这些功能:

void f1(int);
void f2(int,float);

您可以使用其中任何一个调用g:

g(f1); // fine
g(f2); // also fine

然而

g(f); // error

编译器不知道Argsg参数包的用途。