If we have a template function which takes a non-type parameter of type int or short the compiler complains about the ambiguity of the following ca
The compiler error message is missleading*. You would intuitively think that it means that "The function invocation is ambigious!", but in reality the compilation fails in an earlier stage, the definition of the specialized function is not even generated at that point.
What it really means is this: "The function specialization is ambigious!"
Lets see how this compiles:
template void foo() { std::cout << "S: " << S << '\n'; }
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
The first step of the compilation is the template specialization.
Step 1: The compiler realizes that foo<0> is a template specialization, and generates a function declaration accordingly:
template void foo() { std::cout << "S: " << S << '\n'; }
template<>
void foo<0>();
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
Step 2: The compiler relizes that the function is actually invoked (This seems obvious in this case, but it is less obvious when you have a class template.), and generates a definition:
template void foo() { std::cout << "S: " << S << '\n'; }
template<>
void foo<0>();
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
template<>
void foo<0>(){
std::cout << "S: " << 0 << '\n';
}
Step 3: Now you have a callable function, the compilation continues normally.
Lets try to follow the same steps in your case:
template void foo() { std::cout << "S: " << S << '\n'; }
template void foo() { std::cout << "I: " << I << '\n'; }
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
Step 1: Generating the function declaration:
template void foo() { std::cout << "S: " << S << '\n'; }
template void foo() { std::cout << "I: " << I << '\n'; }
template<>
void foo<0>();
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
And at this point the compilation fails, because the specialized declaration of foo is ambigious. If you want proof, try compiling this code:
template void foo() { std::cout << "S: " << S << '\n'; }
template void foo() { std::cout << "I: " << I << '\n'; }
template<>
void foo<0>();
int main(int argc, char* argv[])
{
return 0;
}
You will get the same error message without the function invocation!
Update
So the takeaway is that everything translates to specialized function declarations. So no matter if you write foo of foo, the compiler will generate template<> void foo<0>(); for both. The explicit types will be ignored. (Thats why its really important that they are constexpr-s.)
Update As T.C. pointed out in his comment, in the standard (413rd page in the PDF) there is a very similar example:
[Example: In the following example, assuming a signed char cannot represent the value 1000, a narrowing conversion (8.5.4) would be required to convert the template-argument of type int to signed char, therefore substitution fails for the second template (14.3.2).
templateint f(int); template int f(int); int i1 = f<1000>(0); // OK int i2 = f<1>(0); // ambiguous; not narrowing —end example]
*The error message is completely correct. Might not be particularly intuitive, but it reflects the procedure specified in the standard. – T.C.