使用子类调用时,重载的函数模板匹配模板而不是基类

GEL*_*GEL 0 c++

请考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

产生以下输出:

T&
T&
T*
const T&
const T*
Run Code Online (Sandbox Code Playgroud)

这个输出让我感到惊讶,因为我认为最接近匹配的函数是被调用的函数.所以我期待输出:

A&
A&
A*
const A&
const A*
Run Code Online (Sandbox Code Playgroud)

有人解释为什么当我传入基类(A)的子类(B)时,基类重载选择了模板函数重载?

Nat*_*ica 5

这是预期的行为.你打电话的时候foo(a); aB.所以我们需要从a B到a 的隐式转换A才能调用void foo(A& a).但是既然你也有

template <class T>
void foo(T& a) {
    cout << "T&" << endl;
}
Run Code Online (Sandbox Code Playgroud)

模板被剔除,你得到了void foo(B& a).这是直接匹配,无需转换.这是最好的功能,这就是它被选中的原因.对于所有其他函数来说,这是相同的,并且T推导出B与所有A函数相比更好的匹配.

如果你想停止这个,你可以使用std::enable_if并检查类型是否是派生类std::is_base_of