重载解析,模板和继承

Emi*_*son 20 c++ templates overload-resolution

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

这打印:

Called A
Called template
Run Code Online (Sandbox Code Playgroud)

我的印象是,总是会在模板函数上选择合适的非模板函数.有人可以向我解释导致这种有些令人惊讶的结果的解决步骤吗?

Rei*_*ica 16

我的印象是,总是会在模板函数上选择合适的非模板函数.

这仅适用于模板和非模板同样适合的候选者.这就是选择非模板的原因foo(A()).

但是,在foo(B())使用非模板的情况下,需要派生到基础的转换.因此功能模板严格更好,因此它被选中.

该foo模板实例化到void foo(const B&).考虑一下没有模板会是什么样子:

void foo(const B &x) { std::cout << "Called template" << std::endl; }

void foo(const A &a) { std::cout << "Called A" << std::endl; }
Run Code Online (Sandbox Code Playgroud)

我相信你会同意,foo(B())应该毫不含糊地选择第一个.这就是选择模板的原因.


For*_*veR 5

n3376 13.3.3.1/6

当参数具有类类型且参数表达式具有派生类类型时,隐式转换序列是从派生类到基类的派生到基础的转换.

n3376 13.3.3.1/8

如果不需要转换来将参数与参数类型匹配,则隐式转换序列是由标识转换(13.3.3.1.1)组成的标准转换序列.

身份转换在13.3.3.1.1 /表12中具有完全匹配等级到期表,但是派生到基础比身份更差.

因此,编译器在第一种情况下只有候选者

// template after resolving
void foo(const A&)

// non-template
void foo(const A&)
Run Code Online (Sandbox Code Playgroud)

两者都有身份等级,但由于第一个是功能模板,因此将选择第二个.在第二种情况下

// template after resolving
void foo(const B&)

// non-template
void foo(const A&)
Run Code Online (Sandbox Code Playgroud)

只有第一个具有身份等级并将被选中.