#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;
}
打印:
Called A
Called template
我的印象是,总是会在模板函数上选择合适的非模板函数。有人可以向我解释导致这种有些令人惊讶的结果的解决步骤吗?
答案 0 :(得分: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; }
我相信你会同意,foo(B())
应该毫不含糊地选择第一个。这正是选择模板的原因。
答案 1 :(得分:5)
n3376 13.3.3.1/6
当参数具有类类型且参数表达式具有 派生类类型,隐式转换序列是一个 derived-to-base从派生类到基类的转换。
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&)
两者都有身份等级,但由于第一个是功能模板,因此将选择第二个。 在第二种情况下
// template after resolving
void foo(const B&)
// non-template
void foo(const A&)
只有第一个具有身份等级并将被选中。
答案 2 :(得分:2)
有人可以向我解释导致这种有些令人惊讶的结果的解决步骤吗?
您可以在cppreference.com上查看重载决议: http://en.cppreference.com/w/cpp/language/overload_resolution
特别参见隐式转换序列的排名
部分答案的延伸:
我试图通过上述链接中的信息摘录提供更多说明:
函数模板本身不是类型,函数或任何其他实体。不包含仅包含模板定义的源文件的代码。为了显示任何代码,必须实例化模板:必须确定模板参数,以便编译器可以从类模板生成实际函数(或类)。
为此,编译器会通过:
到此为止,编译器有一些候选函数定义,可以处理特定的函数调用。这些候选者是模板函数的 instannces 以及程序中相关的非模板函数定义。
但你问题的答案实际上就在这里:
模板参数推导发生在函数模板名称查找之后(可能涉及依赖于参数的查找)和之前重载解析。
在模板函数实例化之后执行函数重载解析的事实是代码输出的原因。
现在,您的具体案例将通过重载解析进行如下操作:
重载决议:
如果[previous]步骤产生多个候选函数,则执行重载决策以选择实际调用的函数。通常,参数与参数最匹配的候选函数是被调用的候选函数。 。 。
...
如果F1的所有参数的隐式转换不比F2的所有参数的隐式转换差,则确定F1是比F2更好的函数,并且
1)F1至少有一个参数,其隐式转换优于F2
的该参数的相应隐式转换 ... 。
。
。
隐式转换序列的排名:每种类型的标准转换序列分配三个等级中的一个:
1)完全匹配:无需转换,左值到右值转换,限定转换,用户定义的类类型转换为同一类别
2)推广:整体推广,浮点推广 3)转换:积分转换,浮点转换,浮点积分转换,指针转换,指针到成员转换,布尔转换,用户定义的派生类转换为基础标准转换序列的排名是其持有的标准转化次数中最差的(最多可能有三次转化)
直接将参数参数绑定到参数表达式是Identity或派生到基础的转换:
struct Base {};
struct Derived : Base {} d;
int f(Base&); // overload #1
int f(Derived&); // overload #2
int i = f(d); // d -> Derived& has rank Exact Match
// d -> Base& has rank Conversion
// calls f(Derived&)