27

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".
L. F.
  • 19,445
  • 8
  • 48
  • 82
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • 1
    I think this is intended, because I cannot think of a possible alternative reading. – L. F. Jul 24 '19 at 03:13
  • 4
    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 Jul 24 '19 at 04:07
  • 2
    @Aconcagua Probably the same reason that the default template argument is taken in a non-deduced context https://godbolt.org/z/XjG_D1 – Rakete1111 Jul 24 '19 at 04:21
  • 2
    @Rakete1111 So empty argument list would be considered as 'default'? Appears reasonable somehow... – Aconcagua Jul 24 '19 at 04:40
  • @L.F. I suggested an alternative reading in the last bullet point: it could include cases where deduction was attempted but failed. – Brian Bi Jul 24 '19 at 15:34
  • @Brian Indeed. "Not otherwise deduced" can mean "no deduction at all" or "no successful deduction". – L. F. Jul 25 '19 at 00:39
  • 2
    @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 S* sp_conv(nullptr_t zp) { return zp; } ; and then call g(42, sp_conv(nullptr)); this works with GCC v8 and Clang v7. – jpmarinier Jul 25 '19 at 13:55
  • @jpmarinier I understand why deduction fails. That wasn't what I was asking. – Brian Bi Jul 26 '19 at 21:12

1 Answers1

4

... 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?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
darune
  • 10,480
  • 2
  • 24
  • 62
  • 1
    It sounds like you're making the point that overall deduction only succeeds when deduction succeeds at every deduced context for a given template parameter (or template parameter pack); if there are two deduced contexts for the same template parameter, and deduction fails at one of the two contexts, then the parameter is not deduced according to the other context; instead the deduction simply fails. However, I don't think you answered my question. Could one not read "not otherwise deduced" as covering this situation? The pack was, in a sense, not deduced, since deduction failed. – Brian Bi Aug 30 '19 at 20:33
  • 1
    I guess you are right if read in vacuum it is *somewhat* unclear. Perhaps im predisposed, but I do read it like "Not otherwise deduced" != "deduction was attempted and failed" and "Not other wise deduced" == "deduction was not actually done". Thats mainly due to the "Otherwise" in there. It's not unclear but theres perhaps room for improvement. – darune Aug 30 '19 at 21:13
  • 1
    @darune I would question why standard would use the chiefly British "other wise" ( meaning "in other way") – Swift - Friday Pie Feb 12 '21 at 10:06