21
votes

Consider the following examples (Coliru link):

template <class... T> struct S { using type = int; };

template <class... T>
void f(typename S<T...>::type) {
    static_assert(sizeof...(T) == 0);
}

template <class... T>
void g(typename S<T...>::type, S<T...>*) {}

int main() {
    f(42);
    g(42, nullptr);
}

GCC and Clang are both happy with the call to f, but not the call to g.

In the call to f, although T... appears in a non-deduced context, it ends up being deduced as empty. This seems to be due to [temp.arg.explicit]/4:

... A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments. ...

In the call to g, however, the fact that T... additionally appears in a deduced context, which causes deduction to be attempted and failed, seems to cause g to become non-viable. There seems to be no "fallback" to T... being empty once deduction has been attempted and failed.

  • Is this behaviour intended? If so, why?
  • If so, was it intended that the "not otherwise deduced" wording specifies this behaviour? (i.e., it implies that the empty fallback only occurs if the pack appears in no deduced contexts)
  • If so, is this wording clear enough? It seems a plausible alternative reading of "not otherwise deduced" is "either deduction was not done, or deduction was attempted and failed".
1
I think this is intended, because I cannot think of a possible alternative reading. - L. F.
Completely without templates: void f(int*) { } void f(double*) { } void g() { f(nullptr); } – appears consistent to me (which of the many overloads produced by the template would we actually want to use?). I'd rather turn the question around: What's the reasoning for allowing empty template argument deduction in non-deduced contexts? - Aconcagua
@Aconcagua Probably the same reason that the default template argument is taken in a non-deduced context godbolt.org/z/XjG_D1 - Rakete1111
@Rakete1111 So empty argument list would be considered as 'default'? Appears reasonable somehow... - Aconcagua
@Brian It seems that nullptr_t is a dead end for the type inference system. It has no way to go from nullptr to "a pointer to some sort of S". As a possible workaround, you can provide a hint: template <class... T> S<T...>* sp_conv(nullptr_t zp) { return zp; } ; and then call g(42, sp_conv(nullptr)); this works with GCC v8 and Clang v7. - jpmarinier

1 Answers

2
votes

... A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments. ...

It could be enough to say that the not otherwise deduced is not a clause that should somehow or automagically relax some other rules or actually it is has nothing to do with why it's malformed (contrary to what I think you imply).

This other rule is perhaps best demonstrated by another very simple example:

template<class T>
void f(T, T){};

int main() {
    f(int{42},short{42});
}

The above fails to compile. Why? Because even while short converts to int seamlessly (promotion), it is not the same type.

Additionally since nullptr just has the somewhat plain type of std::nullptr_t - it is very ill-suited to participate in template argument deduction at all.

So let's forget about non-deduced context for a moment, and try with deduced one:

template <class... T>
void g(S<T...>*, S<T...>* ) {}

int main() {
    S<> s1;
    g(&s1, nullptr);
}

or if you prefer, just

int main() {
    S<> s1;
    g(&s1, 0);
}

and both fail for the same reason.

Now, if you would like to allow conversion - then use an identity template - and this even works for the non-deduced context!

For your case, the example could look like ():

template <class... T>
void g(typename S<T...>::type, std::type_identity_t<S<T...> >*) {}

int main() {
    f(42);
    g(42, nullptr);
} 

Which is valid. (note if you don't have have just write the identity template yourself)

As stated in a comment, turning the question around could perhaps lead to a more interesting question ?

What's the reasoning for allowing empty template argument deduction in non-deduced contexts?