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
6
  • Clang is wrong in rejecting the code. Commented Oct 1, 2022 at 16:21
  • Shouldn't the syntax be T v(V...());? Similar to something like V &&... v. Clang accepts this, but GCC doesn't. Commented Oct 1, 2022 at 16:35
  • @HolyBlackCat clang and gcc parse the first parameter as function pointer. Commented Oct 1, 2022 at 16:36
  • @std Yep. I'm not sure what you're getting at. Commented Oct 1, 2022 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. Commented Oct 1, 2022 at 16:48

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Interestingly, both compilers reject T v(V (...)());, treating the parameter as a C-style variadic function returning V().
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.
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());
};

2 Comments

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.
Both GCC and Clang accept V()... (and both reject V...() in template argument list), demo: godbolt.org/z/ohcMvbs5q Why are the rules distinct there?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.