15

I'm learning C++ using the books listed here. In particular I read about variadic templates. Now, to further clear my concepts I'm also writing simple examples and trying to understand them by myself using debugger and cout statements.

One such program that compiles with gcc but is rejected by clang is given below. Demo.

template<typename T, typename... V> 
struct C 
{
    T v(V()...);;
};
int main()
{
    
    C<int> c; //works with gcc but rejected in clang 
    C<int, double, int, int> c2; //same here: works with gcc but rejected in clang
}

So my question is which compiler is right here(if any)?

Here is the error that clang gives:

<source>:6:12: error: '...' must be innermost component of anonymous pack declaration
    T v(V()...);;
           ^~~
          ...
1 error generated.
Compiler returned: 1
Jason
  • 36,170
  • 5
  • 26
  • 60
Alex
  • 318
  • 1
  • 14
  • Clang is wrong in rejecting the code. – Jason Oct 01 '22 at 16:21
  • Shouldn't the syntax be `T v(V...());`? Similar to something like `V &&... v`. Clang accepts this, but GCC doesn't. – HolyBlackCat Oct 01 '22 at 16:35
  • @HolyBlackCat clang and gcc parse the first parameter as function pointer. – mada Oct 01 '22 at 16:36
  • @std Yep. I'm not sure what you're getting at. – HolyBlackCat Oct 01 '22 at 16:38
  • @user17732522 It's a function declaration regardless of the pack, a data member would have to use `{...}` or `=`. But I don't think it's a valid pack expansion, trying to find the right place in the grammar right now. – HolyBlackCat Oct 01 '22 at 16:48
  • @HolyBlackCat If you need a reference for an answer the relevant grammar is https://eel.is/c++draft/dcl.name#nt:noptr-abstract-pack-declarator. The _parameters-and-qualifiers_ follow the _noptr-abstract-pack-declarator_ with the ellipsis. – user17732522 Oct 01 '22 at 16:55

2 Answers2

10

This is a GCC bug. The correct syntax is T v(V...());, which Clang accepts and GCC rejects (incorrectly).

In a function parameter list, the ... that expands a pack must precede the parameter name, or be in the place where the name would otherwise be. This is more commonly seen in cases like V &&... v.

The related grammar is: function-definition -> declarator -> parameters-and-qualifiers -> ... -> parameter-declaration -> abstract-declarator ("abstract" = no parameter name).

[dcl.fct]/26 says that if there's an ambiguity in a function parameter list whether ... is a pack expansion or a C-style variadic parameter, it resolves to a pack expansion. But in this case there's no ambiguity, V()... leaves the pack unexpanded (which should be a compilation error) and ... should be a C-style variadic parameter.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • Interestingly, both compilers reject `T v(V (...)());`, treating the parameter as a C-style variadic function returning `V()`. – HolyBlackCat Oct 01 '22 at 17:28
  • 2
    That seems to be correct. The grammar for _abstract-declarator_ has different terms for the pack variant and the non-pack variant and only the non-pack variant has a term to parenthesize the (ptr-)declarator. – user17732522 Oct 01 '22 at 18:37
5

GCC is wrong in accepting the program because a function parameter pack is introduced using an ellipsis (...) prior to (or in the place of) the function parameter name.

This means in your example the correct way to declare the function with parameter of type pointer to function is T v(V...());. But note that gcc rejects this modified code. Demo

Here is the gcc bug report:

GCC accepts invalid program involving function declaration with pack expansion

Note

It quite interesting to note that gcc rejects T v(V...()) but accepts T v(V...b()). Demo

template<typename T, typename... V> 
struct C 
{
//-------vvv---------->this is the correct way which gcc rejects 
    T v(V...());
//-------vvv---------->but gcc accepts this when we name the parameter
    T v(V...b());
};
Jason
  • 36,170
  • 5
  • 26
  • 60
  • 3
    Shouldn't the syntax be `T v(V...());`? Similar to something like `V &&... v`. Clang accepts this, but GCC doesn't. Looks more like a GCC bug to me. – HolyBlackCat Oct 01 '22 at 16:35
  • Both GCC and Clang accept `V()...` (and both reject `V...()` in template argument list), demo: https://godbolt.org/z/ohcMvbs5q Why are the rules distinct there? – Fedor Oct 02 '22 at 19:28