Consider this piece of code:
template<typename FirstArg>
void foo()
{
}
template<typename FirstArg, typename... RestOfArgs>
void foo()
{
foo<RestOfArgs...>();
}
int main()
{
foo<int, int, int>();
return 0;
}
It does not compile due to ambiguous call foo<RestOfArgs...>();
when RestOfArgs
has only one element ({int}
).
But this compiles without error:
template<typename FirstArg>
void foo(FirstArg x)
{
}
template<typename FirstArg, typename... RestOfArgs>
void foo(FirstArg x, RestOfArgs... y)
{
foo(y...);
}
int main()
{
foo<int, int, int>(5, 6, 7);
return 0;
}
Why is there ambiguity in the first case?
Why is there no ambiguity in the second case?
In Function template overloading
There is lot of rules to tell which template functions is more specialized (according to given parameters).
The point which makes
template<typename> void foo();
template<typename, typename...> void foo();
ambiguous for foo<int>()
, but not
template<typename T> void foo(T);
template<typename T, typename... Ts> void foo(T, Ts...);
for foo(42)
is the following:
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.
Why is there ambiguity in the first case?
RestOfArgs
can be empty.
So foo<int>
can be instantiated as:
template<int>
void foo()
{
}
and
template<int,>
void foo()
{
foo<>();
}
both will compile, so it is ambiguous.
Actually foo<>()
won't compile, but it fails in the next instantiation, so it doesn't matter.
Why is there no ambiguity in the second case?
foo<int>(7)
can be instantiated as:
template<int>
void foo(int 7)
{
}
and
template<int>
void foo(int 7)
{
foo();
}
but the second one is an error, because there are no foo taking no argument, so the only candidate is the first one, so there won't be ambiguous
The answer by @ZangMingJie answers the difference in behavior your are observing in your code.
I found it easier to understand the name resolution with the following change:
template<typename FirstArg>
void foo()
{
printf("1\n");
}
template<typename FirstArg, typename SecondArg, typename... RestOfArgs>
void foo()
{
printf("2\n");
foo<SecondArg, RestOfArgs...>();
}
int main()
{
foo<int, int, int>();
return 0;
}
When there are two or more template parameters are used, the second function gets invoked. When there is one template parameters, the first function gets invoked.