Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload resolution, templates and inheritance

Tags:

#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?

like image 815
Emil Eriksson Avatar asked Feb 09 '15 08:02

Emil Eriksson


2 Answers

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.

like image 133
Angew is no longer proud of SO Avatar answered Sep 23 '22 06:09

Angew is no longer proud of SO


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.

like image 29
ForEveR Avatar answered Sep 20 '22 06:09

ForEveR