Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What exactly is a "trailing parameter pack"

In resolving ambiguities between function template overloads, partial ordering is performed (see here for some explanations). In that website, we also learn that

In case of a tie, if one function template has a trailing parameter pack and the other does not, the one with the omitted parameter is considered to be more specialized than the one with the empty parameter pack.

Now, I wonder what precisely a trailing parameter pack is. Which if any of

template<class ...> struct tuple { /* ... */ };

template<class T, class...Ts> void foo(tuple<T,Ts...>);

template<class T, class...Ts> void bar(T, Ts...);

is and which not and why? Note also that clang considers

template<class T> void f(tuple<T>);

template<class T, class...Ts> void f(tuple<T,Ts...>);

int main()
{  f(tuple<int>());  }   // ambiguous call?

ambiguous, implying that foo does not have a trailing parameter pack.

like image 703
Walter Avatar asked Dec 09 '16 14:12

Walter


2 Answers

This is CWG1395, for which a defect resolution was recently voted in to the draft C++17 standard. The following was added to [temp.deduct.partial]:

...[if] function template F is at least as specialized as function template G and vice-versa, and if G has a trailing parameter pack for which F does not have a corresponding parameter, and if F does not have a trailing parameter pack, then F is more specialized than G.

The standard doesn't explicitly define what it means by "trailing parameter pack", but judging by the existing contexts in which this term is used, it refers to a template parameter pack that appears as the rightmost parameter in a template parameter list:

template<class T, class... U> struct X;
//                ^^^^^^^^^^

Or, a function parameter pack that appears as the rightmost parameter in a function parameter list:

template<class T, class... U> void y(T, U...);
//                                      ^^^^

The current draft still contains this outdated example in [temp.deduct.type]:

template<class T, class... U> void f(T, U...);
template<class T> void f(T);

f(&i); // error: ambiguous

This standard defect report has been around for a few years, and both GCC and Clang have implemented resolutions of it. They both agree that the example above is a valid call of the second overload of f.

Where GCC and Clang disagree is on the scope of the defect resolution. This is understandable, as it was only recently updated to include proposed standard wording. In your example, the pack is not expanded into the function parameter list, but into the template argument list of a function parameter type:

template<class T, class... U> void g(tuple<T, U...>);
template<class T> void g(tuple<T>);

g(tuple<int>{});

GCC treats this as a valid call of the second overload of g; Clang treats it as ambiguous. The correctness of Clang may depend on whether "trailing parameter pack" is intended to include trailing template parameter packs, or only trailing function parameter packs.

Note that both compilers agree that C<int> refers to the second partial specialization of the class template C in the following example:

template<class...> struct C;

template<class T, class... U> struct C<T, U...> {};
template<class T> struct C<T> {};

This seems like an inconsistency in Clang, because the standard rules for partial ordering of class template specializations is defined in terms of partial ordering of function templates. See CWG1432.

like image 172
Oktalist Avatar answered Oct 22 '22 03:10

Oktalist


Trailing means "at the end of".

A trailing parameter pack is a parameter pack found at the end of a list of template parameters:

template <typename T1, typename... Ts>
void foo();

// ^ Ts... is trailing here

This is not a C++ question, but an English question.

like image 23
Lightness Races in Orbit Avatar answered Sep 30 '22 01:09

Lightness Races in Orbit