#include <iostream> struct A {}; struct B : public A {}; template<typename T> void foo(const T &x) { std::cout << "Called template" << std::endl; } void foo(const A &a) { std::cout << "Called A" << std::endl; } int main() { foo(A()); foo(B()); return 0; }
This prints:
Called A Called template
I was under the impression that a suitable non-template function would always be chosen over a template function. Can someone explain to me the resolution steps that lead to this somewhat surprising result?
I was under the impression that a suitable non-template function would always be chosen over a template function.
This only holds if the template and the non-template are equally good candidates. That's why the non-template is chosen for foo(A())
.
However, in the case of foo(B())
, using the non-template requires a derived-to-base conversion. So the function template is strictly better, and hence it's chosen.
The foo
template instantiates into void foo(const B&)
. Consider what it would look like without templates:
void foo(const B &x) { std::cout << "Called template" << std::endl; } void foo(const A &a) { std::cout << "Called A" << std::endl; }
I believe you'll agree calling foo(B())
should unambiguously pick the first one. That's exactly why the template is chosen.
n3376 13.3.3.1/6
When the parameter has a class type and the argument expression has a derived class type, the implicit conversion sequence is a derived-to-base Conversion from the derived class to the base class.
n3376 13.3.3.1/8
If no conversions are required to match an argument to a parameter type, the implicit conversion sequence is the standard conversion sequence consisting of the identity conversion (13.3.3.1.1).
Identity conversion has exact match rank due table in 13.3.3.1.1/table 12, but derived-to-base is worse, than identity.
So, compiler just have candidates in first case
// template after resolving void foo(const A&) // non-template void foo(const A&)
Both has identity rank, but since first is function-template, second will be chosen. And in second case
// template after resolving void foo(const B&) // non-template void foo(const A&)
Only first has identity rank and will be chosen.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With