I have the following piece of code:
template <typename, typename>
struct AAA{};
template<typename ...Args>
void f(AAA<Args...> *) {}
int main() {
f<int, int>(nullptr);
}
This code results in a compile error. When compiling using g++ -std=c++1z
the error shows as follows:
prog.cc: In function 'int main()':
prog.cc:8:24: error: no matching function for call to 'f<int, int>(std::nullptr_t)'
f<int, int>(nullptr);
^
prog.cc:5:6: note: candidate: template<class ... Args> void f(AAA<Args ...>*)
void f(AAA<Args...> *) {}
^
prog.cc:5:6: note: template argument deduction/substitution failed:
prog.cc:8:24: note: mismatched types 'AAA<Args ...>*' and 'std::nullptr_t'
f<int, int>(nullptr);
Using clang++ -std=c++1z
the error is:
prog.cc:8:5: error: no matching function for call to 'f'
f<int, int>(nullptr);
^~~~~~~~~~~
prog.cc:5:6: note: candidate template ignored: could not match 'AAA<int, int, Args...> *' against 'nullptr_t'
void f(AAA<Args...> *) {}
^
1 error generated.
I am running those above in a MSYS2 MinGW-w64 environment. My GCC version is GCC 7.1.0 and my Clang version is 4.0.0; the standard library I use both in GCC and in Clang is the libstdc++ bundled with my GCC compiler.
In my opinion, the call to function template foo
has its template parameter explicitly specified thus the template parameter pack and the function argument type should already be specified. However, the error diagnostics shown above seem to suggest that the exact type of function parameter and the nullptr
argument does not match, which seems to be a issue only possible when function argument deduction occurs. So my question is, why does such error occur? Is it just a compiler bug, or does the C++ standard have some rules that indicate the original code is just ill-formed?
Just to add an easy solution:
The latter forces the pack
Args...
to be{int, int}
, and now the call itself isn't a call to a function template - it's just a call to a function pointer. We're calling a function that takes anAAA<int, int>*
, and of course passing innullptr
is acceptable there.For fun, you can also add arbitrarily many
*
s:... but, you know... don't.
Great answer by @skypjack.
You need to help the compiler in deducing the function argument:
Fundamentally,
nullptr
represents a "no object" which can be assigned to any pointer type.I want to add another solution that invokes the
{}
notionWhen the argument is
{}
, deduction for the parameter is disabled (nondeduced context), so there will be no mismatch and the parameter initialization actually produces a null pointer aswell.For you are using
typename ...Args
, the compiler doesn't know ifint, int
are all the template parameters in use or more are available by deducing the function argument. Therefore the function isn't instantiated yet and the compiler goes on trying to deduce all the other possible parameters for the parameter pack from the function arguments.In other terms, this works:
Because you are saying that the first parameter is
int
, but the compiler expects a parameters list and goes on trying to find more and more parameters greedily from the function argument, then it instantiates the function template.More or less the same happens in your case, but the compiler cannot deduce anything from
nullptr_t
for function arguments doesn't match. It expects a pointer to anA<...>
, that is not the case when you pass innullptr
.This would work instead:
Because the compiler knows template arguments are two and you are providing all of them, there is nothing to deduce and the function can be instantiated. It also makes much more sense, for
AAA
accepts only two template parameters, so a parameter pack forf
seems useless here.You may think the compiler should deduce the pack as
int ,int
, but the C++ standard explicitly requires the behavior you observed.[temp.arg.explicit/9]
The above means that even though some of the parameters were specified, deduction doesn't end. The parameter pack must always be expandable by argument deduction. It's as if the explicit arguments that were given are an instantiation of a template with a trailing parameter pack. When coupled with the following:
[temp.arg.explicit/3]
The compiler must match the un-deduced arguments to an empty pack. But it has nothing to deduce it from.
As such, your attempt to plug
Args...
intoAAA
cannot possibly match. Because the type sequence is two types with a trailing list (that the compiler cannot deduce as empty fromnullptr
). WhileAAA
expects just two types.