Reputation: 119239
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.
Upvotes: 27
Views: 976
Reputation: 10982
... 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 (c++2a):
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 c++20 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?
Upvotes: 4