GEL GEL - 3 months ago 17
C++ Question

Overloaded function template matching template instead of base class when called with subclass

Consider the following code:

#include <cstdlib>
#include <iostream>

using std::cout;
using std::endl;

class A {
public:
virtual ~A() {

}
};

class B : public A {

};

void foo(A& a) {
cout << "A&" << endl;
}

void foo(const A& a) {
cout << "const A&" << endl;
}

void foo(A* a) {
cout << "A*" << endl;
}

void foo(const A* a) {
cout << "const A*" << endl;
}

template <class T>
void foo(T& a) {
cout << "T&" << endl;
}

template <class T>
void foo(const T& a) {
cout << "const T&" << endl;
}

template <class T>
void foo(T* a) {
cout << "T*" << endl;
}

template <class T>
void foo(const T* a) {
cout << "const T*" << endl;
}

int main(int argc, char** argv) {
B a;
foo(a);

B& b = a;
foo(b);

B* c = &a;
foo(c);

const B& d = a;
foo(d);

const B* e = &a;
foo(e);

return EXIT_SUCCESS;
}


Produces the following output:

T&
T&
T*
const T&
const T*


This output surprised me because I thought the function that was the closest match would be the one invoked. So I was expecting the output:

A&
A&
A*
const A&
const A*


Can someone explain why the template functions overloads are chosen over the base class overloads when I pass in a subclass (B) of the base class (A)?

Answer

This is the expected behavior. When you call foo(a); a is a B. So we would need a implicit conversion from a B to a A in order to call void foo(A& a). However since you also have

template <class T>
void foo(T& a) {
    cout << "T&" << endl;
}

The template gets stamped out and you get void foo(B& a). This is a direct match and requires no conversion. This is the best function so that is why it is picked. This is the same for all of the other functions as well as T gets deduced to B which gives a better match then all of the A functions.

If you want to stop this you could use std::enable_if and check if the type is a derived class with std::is_base_of

Comments