16

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.

max66
  • 65,235
  • 10
  • 71
  • 111
Walter
  • 44,150
  • 20
  • 113
  • 196
  • 1
    Lol, these two comments contradict each other! So there is indeed something to discuss here. – Walter Dec 09 '16 at 14:24
  • @Jarod42 So you say that 'trailing parameter pack' refers to the function arguments, not the template parameters? – Walter Dec 09 '16 at 14:25
  • Indeed clang and gcc disagree [Demo](http://coliru.stacked-crooked.com/a/6f67a0a696424a56). – Jarod42 Dec 09 '16 at 14:38

2 Answers2

7

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.

Oktalist
  • 14,336
  • 3
  • 43
  • 63
  • 2
    A resolution for CWG1395 has just been voted into the latest working draft, see [N4619](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4619.html). I suppose that's where the cppreference text comes from. Unfortunately, it doesn't seem to address 1432, and that's not the only issue I see there. For more fun, check out [this discussion](https://chat.stackoverflow.com/rooms/130228/discussion-between-bogdan-and-barry). – bogdan Dec 09 '16 at 23:42
  • Just did a quick test, and MSVC agrees with GCC: [the `template` version beats the `template` version in both cases](http://rextester.com/FEKSB40747). I'm honestly a tad surprised that they resolved the `g()` version faster than Clang did. – Justin Time - Reinstate Monica Dec 10 '16 at 00:04
  • Are there any parameter packs which are not trailing? – M.M Dec 10 '16 at 02:04
  • @M.M There is no trailing parameter pack in `template void h(tuple)` – Oktalist Dec 10 '16 at 03:14
  • Note that GCC treats `template void g(tuple)` in exactly the same way as `template void g(tuple)` in your example, which seems to contradict your answer to @M.M above. In all contexts where partial ordering of function templates is used, the algorithm compares patterns that appear in function parameters, return types or whole function types, that's it; never the template parameter lists themselves. Whether a pack is trailing in the template parameter list is irrelevant in this context. – bogdan Dec 10 '16 at 10:28
  • @bogdan that sounds sensible, but can you back that up by more than your own authority? – Walter Dec 10 '16 at 14:15
  • 1
    @Walter My own authority isn't enough to convince even myself of anything, so I've been trying to provide some logical arguments. I think our best bet for an authoritative response is for someone from CWG to show up either here or on [std-discussion](https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/zTTjuPlXJ2A) and tell us what their intent was when they drafted that new wording. And whatever that was, I think there's still some more work that needs to be done on the wording, as discussed in chat. – bogdan Dec 10 '16 at 16:24
  • @bogdan I don't think the behaviour of GCC can tell us anything about the intent of the committee in this case. You will probably turn out to be correct that "trailing parameter pack" is only intended to mean trailing function parameter pack, but in the absence of an authoritative clarification I am taking the wording at face value. The `g` example should eventually be fixed by 1432, but the question was about wording that came from 1395. – Oktalist Dec 10 '16 at 22:17
  • @bogdan perhaps the authors of the standard and its drafts thought that the meaning of 'trailing parameter pack' was completely obvious and perhaps your argument is the 'obvious' reason why. The problem with the wording is that 'parameter' appears to refer to template *parameters* rather than function *arguments*. – Walter Dec 11 '16 at 00:01
  • @Walter The standard is supposed to be a formal definition. I think the omission of an "obvious" definition would simply be a reflection of the committee's failure to express what they meant, which in turn could indicate that even they didn't know what they meant. It reminds me of John Lakos' [war story](https://youtu.be/BshgPboz_AQ?t=7m20s) from Kona '07 concerning [LWG684](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#684) and the committee's failure to agree on what it meant for two objects to be "the same". – Oktalist Dec 11 '16 at 01:35
  • @Walter Throughout the standard, there's a clear distinction between "parameter" and "argument", both for function parameters and for template parameters; they're not interchangeable. Function *arguments* only appear here when partial ordering is done in the context of a function call, and then only to select which *function parameters* from each function template are used to run the algorithm. Other than that, only function *parameter* types are used, so there's no reason for "parameter" to mean "argument", but that doesn't make "template parameter" a more likely interpretation in any way. – bogdan Dec 11 '16 at 10:04
  • This issue is fixed by [an editorial change](https://github.com/cplusplus/draft/commit/8c50cba4d55450575a8faa24173f08cd16dfb46e). – xskxzr Nov 18 '18 at 17:18
0

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.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055