What is the meaning of "... ..." token? i.e. double ellipsis operator on parameter pack
Asked Answered
Z

2

118

While browsing through gcc's current implementation of new C++11 headers, I stumbled upon "......" token. You can check, that the following code compiles fine [via godbolt.org].

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

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

So, what is the meaning of this token?

edit: Looks like SO trimmed "......" in question title to "...", I did really mean "......" . :)

Zoonosis answered 11/4, 2011 at 18:15 Comment(5)
hint: it is ... followed by ... .Moose
Is it not more like U... followed by ... . Very odd nonetheless.Nga
Note: This can be found in <functional> and <type_traits>, always in the context of a function argument list inside a template parameter.Trews
only way I found to make it stuck in the title was to put a space inbetween... hope it makes it clearer for the readers.Strategist
@Matthieu M.: Thanks, much better!Zoonosis
T
81

Every instance of that oddity is paired with a case of a regular single ellipsis.

  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; };

My guess is that the double ellipsis is similar in meaning to _ArgTypes..., ..., i.e. a variadic template expansion followed by a C-style varargs list.

Here's a test supporting that theory… I think we have a new winner for worst pseudo-operator ever.

Edit: This does appear to be conformant. §8.3.5/3 describes one way to form the parameter list as

parameter-declaration-listopt ...opt

So the double-ellipsis is formed by a parameter-declaration-list ending with a parameter pack, followed by another ellipsis.

The comma is purely optional; §8.3.5/4 does say

Where syntactically correct and where “...” is not part of an abstract-declarator, “, ...” is synonymous with “...”.

This is within an abstract-declarator, [edit] but Johannes makes a good point that they are referring to an abstract-declarator within a parameter-declaration. I wonder why they didn't say "part of a parameter-declaration," and why that sentence isn't just an informative note…

Furthermore, va_begin() in <cstdarg> requires a parameter before the varargs list, so the prototype f(...) specifically allowed by C++ is useless. Cross-referencing with C99, it is illegal in plain C. So, this is most bizarre.

Usage note

By request, here is a demonstration of the double ellipsis:

#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 );
}
Trews answered 11/4, 2011 at 18:32 Comment(10)
Yes, that's right. T(U..., ...) however compiles fine, too; perhaps they wanted to save some space. :)Zoonosis
But what would that mean? And how can the compiler tell where _ArgTypes end and some "extra" parameters start?Fronton
@Bo Persson: std::is_function's value must be true even if the function is C varargs one and because T(U...) is not match for such function, you need this madness. E.g. int f(int, char, ...) matches T(U......) exactly with T = int, U = {int, char} and the "..." varargs token.Zoonosis
@Zoonosis - Ok, thanks I guess. I was just considering a new question "When is this useful?" to earn my first Tubleweed badge. No luck there, obviously. :-)Fronton
"This is within an abstract-declarator" -> they mean not part of the abstract declarator of the last parameter of that same parameter type list. E.g void (int...) here, the ... is not part of the abstract-declarator int, hence it is synonymous to void(int, ...). If you would write void(T...) and T is a template parameter pack, ... would be part of the abstract-declarator, and hence it would not be equivalent to void(T, ...).Gannie
@Johannes: Ah, I think they could have been clearer about that. Does that sentence have any actual effect? It probably should be moved into the [Note:] block that follows.Trews
Could you show some code that actually calls a function with this ...... notation, and what it means?Gunthar
I am not a c++ standard doc lawyer, Can you please tell me what do you mean by "within an abstract -declarator"Dispassionate
@Dispassionate "abstract-declarator" is just a grammar production with little significance, except that it may include a .... It is "abstract" because it does not include any name to be declared, although in the context of a function declaration a non-abstract declarator (with a name) is also acceptable. If a name is supplied, though, it would go between the two ellipses.Trews
"Furthermore, va_begin() in <cstdarg> requires a parameter before the varargs list, so the prototype f(...) specifically allowed by C++ is useless." -- It's only useless if you want to know what arguments were passed. f(...) is used heavily as a fallback function overload in template metaprogramming, where this information is not necessary (and where the function doesn't even actually get called).Typesetting
E
4

on vs2015 separating comma is essential in the template version:

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

an example instantiation is:

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

regards, FM.

Exploitation answered 26/5, 2016 at 17:40 Comment(3)
I just noticed this too, it still happens. Bug report at developercommunity.visualstudio.com/content/problem/437260/….Disestablish
Good to know. Any references or citations to standads about this?Exploitation
OK then. I'm no expert on the standard - I think that others have covered it in some detail above. If anyone cares to comment on the the Microsoft problem report then it might raise its priority. The report shows clang and gcc allowing what VC++ doesn't so I think we're probably on fairly strong ground.Disestablish

© 2022 - 2024 — McMap. All rights reserved.