22

While looking at this question I found myself in the cpp reference site where I noticed a strange and new to me syntax :

template<class Ret, class... Args>
struct is_function<Ret(Args......)volatile &&> : std::true_type {};

Yep, 6 dots ! Initially I thought this was a typo, but after checking the libstdc++ source again there it was eg at line 444 :

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

Is this a valid syntax ? Dot dot dot, are used to pack and unpack parameter packs ? What do 6 dots do ?

Community
  • 1
  • 1
Lorah Attkins
  • 5,331
  • 3
  • 29
  • 63
  • So the question we were discussing yesterday was merged with this one, and my recommendation still stands. You can still update the question to focus on why this feature is being used with `is_function` instead of focusing on validity of the syntax and I feel like this makes it a non-duplicate. – Shafik Yaghmour Nov 04 '15 at 16:48

2 Answers2

65

Why does libstdc++ use ... ... in it's implementation of is_function? If we check out the cppreference section for std::is_function it gives a sample implementation and says for the first ... ... case:

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

so we need the second set of ... to match a variadic function like 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

Note, we have functions like fprintf that two arguments before the variadic terms and we need to match those as well. Indeed if we use that implementation and attempt to match printf without the ... ... specialization then it fails see it live.

This corner of the language is covered in this post C++11's six dots:

I was mucking around the other day and discovered this nice little oddity:

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

As it turns out, ...... can be totally valid C++11. This is what happens when backward compatibility mixes with new hotness.

// These are all equivalent.

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

Hopefully the last one shows what is happening here. [...]

Why is this valild? We can see that , ... is synonymous with ... from the draft C++11 standard section 8.3.5 [dcl.fct] which has the following grammar:

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

and says:

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

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 4
    I suppose the big question is: Why is `libstdc++` using six dots here? – PythonNut Nov 03 '15 at 17:16
  • @PythonNut to specialize for variadic functions like `printf` and `fprintf` cppreference has a [sample implementation for is_function](http://en.cppreference.com/w/cpp/types/is_function) which makes this comment. If we take the sample and remove the `... ...` versions it no longer matches printf etc.... [see it live](http://melpon.org/wandbox/permlink/U8NogUTEsXPzOrft). – Shafik Yaghmour Nov 03 '15 at 17:39
  • @LorahAttkins you can always edit your question to further differentiate from the duplicate as well and try and get it reopen that way as well. I don't see any reason why that should not work. – Shafik Yaghmour Nov 04 '15 at 10:04
10

In this case, the two are for different purposes. The first is for parameter pack expansion and the second is for variable argument lists. That particular declaration is to handle functions which take some regular parameters plus a variable argument list.

The difference is between run-time and compile-time variability. A function which takes a variable number of arguments at run-time is special. It is a single function which can handle a variable number of arguments from the caller:

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

This is distinct from the notion that we want to be able to have a template which uses a variety of functions with various parameters which are known at compile time. For example, we could define a function template which takes a pointer to a function and allow the number and types of the arguments to vary:

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

Given these functions:

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

You can call g with any of them:

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

However

g(f); // error

The compiler wouldn't know what to use for the Args parameter pack in g.

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • The fact that variadic arguments are not enough to express variability still bothers me (I mean what's the point; if we say that a variable number of args is followed by a variable number of args, how can the compiler know what's what?) but I guess I'll look into the linked material better. Thnx for the quick answer (and thnx to T.C. for providing an existing solution) – Lorah Attkins Dec 21 '14 at 23:24
  • I think you're missing a comma in the first example code? – Ben Voigt Dec 22 '14 at 00:50
  • @BenVoigt: The comma is optional. I thought leaving it out might make it more clear how `......` could be legal. – Vaughn Cato Dec 22 '14 at 03:24
  • 8
    @VaughnCato: Yeah, it does make that point. Still, any programmer writing `......` with neither a comma nor whitespace separating the two (and I definitely prefer a comma) should be subjected to an abject lesson in why good coding style matters. For that purpose, I propose being forced to read the entire linux kernel codebase. – Ben Voigt Dec 22 '14 at 04:49