user2023370
user2023370

Reputation: 11037

Legal use of non-trailing function template parameter pack?

While debugging someone's nasty macro-generated code, I saw that MSVC++ alone was not happy with something akin to the following function template declaration:

template <typename ...Vs, typename>
void foo();

Fair enough. I was puzzled though as to why GCC and Clang would compile that. I added a definition of foo to the declaration above, and now GCC also produces a compilation error (Clang remains content). This code is below:

template <typename ...Vs, typename>
void foo();

template <typename ...Vs, typename = int>
void foo() {  }

int main(int argc, char *argv[])
{
  foo<char,float>();
  return 0;
}

Is Clang right or wrong? I note that GCC and MSVC++ can compile if the declaration is removed.

Upvotes: 5

Views: 243

Answers (2)

max66
max66

Reputation: 66210

Not sure but seems to me that g++ is right and clang++ is wrong.

songyuanyao is right saying that a template parameter isn't necessarily in last position when the "following parameters [...] have default arguments".

But g++ agree with this because if you add the default argument in the declaration, removing it from the definition,

template <typename ...Vs, typename = int>
void foo();

template <typename ...Vs, typename>
void foo() {  }

int main ()
{
  foo<char,float>();
}

both compilers (also g++) compile without problems.

So the point, IMHO, is: can we express template default arguments also in the function definition (function definition only; when preceded by a function declaration) or only in function declaration(s) ?

Isn't completely clear to me but, reading the C++17 standard, i see (17.1.9)

A default template-argument may be specified in a template declaration. A default template-argument shall not be specified in the template-parameter-list s of the definition of a member of a class template that appears outside of the member’s class. A default template-argument shall not be specified in a friend class template declaration. If a friend function template declaration specifies a default template-argument , that declaration shall be a definition and shall be the only declaration of the function template in the translation unit

So default template arguments are surely accepted in "declaration"s, not in "definition".

I repeat: not sure because is explicitly excluded in case of a definition of "a member of a class template that appears outside of the member’s class", not in case of a generic definition after a declaration.

Anyway, I don't see explicated that a default template argument can be accepted in a definition that is preceded by a declaration.

In 17.1.10 we have

The set of default template-argument s available for use is obtained by merging the default arguments from all prior declarations of the template in the same way default function arguments are

Again: "declarations", not "definition".

So I suppose that g++ is wrong because a default template-argument shouldn't be accepted in a definition after a declaration.

Upvotes: 1

songyuanyao
songyuanyao

Reputation: 172934

Clang is correct. The template parameter pack may appear earlier if the following parameters have default arguments.

In a primary class template, the template parameter pack must be the final parameter in the template parameter list. In a function template, the template parameter pack may appear earlier in the list provided that all following parameters can be deduced from the function arguments, or have default arguments:

template<typename... Ts, typename U> struct Invalid; // Error: Ts.. not at the end

template<typename ...Ts, typename U, typename=void>
void valid(U, Ts...);     // OK: can deduce U
// void valid(Ts..., U);  // Can't be used: Ts... is a non-deduced context in this position

valid(1.0, 1, 2, 3);      // OK: deduces U as double, Ts as {int,int,int}

Then given foo<char,float>();, Vs is deduced as char, float, and the 2nd template argument would be int.

From the standard, [temp.param]/13

If a template-parameter of a primary class template, primary variable template, or alias template is a template parameter pack, it shall be the last template-parameter. A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list ([dcl.fct]) of the function template or has a default argument ([temp.deduct]).

And about the behavior of gcc and msvc,

GCC and MSVC++ will successfully compile if the declaration is removed.

Gcc and MSVC seem failing to merge the default template arguments, which appear in the declarations and the definition should be merged.

Default template arguments that appear in the declarations and the definition are merged similarly to default function arguments:

template<typename T1, typename T2 = int> class A;
template<typename T1 = int, typename T2> class A;
// the above is the same as the following:
template<typename T1 = int, typename T2 = int> class A;

Upvotes: 5

Related Questions