“……”标记的含义是什么?即参数包上的双省略号运算符


110

浏览gcc当前对新C ++ 11标头的实现时,我偶然发现了“ ......”标记。您可以[通过ideone.com] 检查以下代码是否可以正常编译

template <typename T>
struct X
{ /* ... */ };

template <typename T, typename ... U>
struct X<T(U......)> // this line is the important one
{ /* ... */ };

那么,该令牌的含义是什么?

编辑:看起来像这样将标题中的“ ......”修饰为“ ...”,我的意思是“ ......”。:)


提示:...后跟...
Alexandre C.

5
是不是更像是U...紧随其后的...。但是很奇怪。
edA-qa mort-ora-y

1
注意:可以在<functional>和中找到它<type_traits>,总是在模板参数内的函数参数列表的上下文中。
Potatoswatter 2011年

我发现让它停留在标题中的唯一方法是在两者之间留一个空格...希望它能使读者更清楚。
Matthieu M.

@Matthieu M .:谢谢,好多了!
Vitus

Answers:


79

该奇数的每个实例都与一个规则的单个省略号配对。

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes...)>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes......)>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes...) const>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes......) const>
    { typedef _Res result_type; };

我的猜测是,双省略号的含义类似于,即可_ArgTypes..., ...变参数模板扩展,后跟C样式的变量列表。

这是支持该理论的测试 ……我认为我们是有史以来最糟糕的伪运算符的新赢家。

编辑:这似乎是一致的。§8.3.5/ 3描述了一种形成参数列表的方式:

参数声明列表opt ... opt

因此,双省略号由一个以参数包结尾的参数声明列表组成,后跟另一个省略号。

逗号是完全可选的;§8.3.5/ 4确实说

在语法上正确,并且“ ...”不是抽象声明符的一部分时,“,...”与“ ...”同义。

在抽象声明器中,[edit]但是Johannes提出了一个很好的观点,即他们在参数声明中引用了抽象声明器。我想知道为什么他们不说“参数声明的一部分”,为什么那句话不只是内容丰富的注释……

此外,va_begin()in <cstdarg>在varargs列表之前需要一个参数,因此f(...)C ++专门允许的原型是无用的。与C99交叉引用在普通C语言中是非法的。因此,这是最奇怪的。

使用说明

根据要求,以下双省略号的演示

#include <cstdio>
#include <string>

template< typename T >
T const &printf_helper( T const &x )
    { return x; }

char const *printf_helper( std::string const &x )
    { return x.c_str(); }

template< typename ... Req, typename ... Given >
int wrap_printf( int (*fn)( Req... ... ), Given ... args ) {
    return fn( printf_helper( args ) ... );
}

int main() {
    wrap_printf( &std::printf, "Hello %s\n", std::string( "world!" ) );
    wrap_printf( &std::fprintf, stderr, std::string( "Error %d" ), 5 );
}

是的,这是对的。T(U ...,...)也可以编译。也许他们想节省一些空间。:)
Vitus

1
但是那是什么意思呢?编译器如何知道_ArgTypes的结束位置和一些“额外”参数的起始位置?
Bo Persson

12
@Bo佩尔森:std::is_functionvalue,即使该函数是C可变参数之一,因为T(ü...)的必须是真实的匹配这样的功能,你需要这种疯狂。例如,int f(int,char,...)与T(U ......)完全匹配,其中T = int,U = {int,char}和“ ...” varargs标记。
Vitus

4
“这一个抽象说明符中” - >他们的意思不是同一参数类型列表的最后一个参数的抽象宣告的一部分。例如,void (int...)此处...不是abstract-declarator的一部分int,因此它是的同义词void(int, ...)。如果您要编写void(T...)并且T是模板参数包,...那么它将成为abstract-declarator的一部分,因此它不等同于void(T, ...)
Johannes Schaub-litb 2011年

2
“此外,<cstdarg>中的va_begin()在varargs列表之前需要一个参数,因此C ++专门允许的原型f(...)是没有用的。” -仅当您想知道传递了哪些参数时才有用。f(...)在模板元编程中被大量用作后备函数重载,在该情况下,此信息不是必需的(并且实际上甚至不会调用该函数)。

4

vs2015上的逗号分隔在模板版本中至关重要:

    template <typename T, typename ... U>
    struct X<T(U...,...)> {};// this line is the important one

示例实例是:

    X<int(int...)> my_va_func;

问候,FM。


我也注意到了这一点,它仍然会发生。错误报告位于developercommunity.visualstudio.com/content/problem/437260/…
egyik

很高兴知道。关于标准的任何参考或引用吗?
Red.Wave

.سلامببخشیدنمیدانم
egyik

这是一个公开论坛。让人们读懂您的想法。PLZ保留本地lang用于私人消息。سپاس。
Red.Wave,

那好吧。我不是该标准的专家-我认为其他人已经在上面进行了详细介绍。如果有人想对Microsoft问题报告发表评论,那么它可能会提高其优先级。该报告显示clang和gcc允许使用VC ++所不具备的功能,因此我认为我们可能处于相当有利的地位。
egyik '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.