C++ 重载解析、模板和继承

C++ 重载解析、模板和继承,c++,templates,overload-resolution,C++,Templates,Overload Resolution,我的印象是,总是会选择一个合适的非模板函数而不是模板函数。有人能给我解释一下导致这个令人惊讶的结果的解决步骤吗 我的印象是,总是会选择一个合适的非模板函数而不是模板函数 只有当模板和非模板都是同样好的候选对象时,这一点才成立。这就是为foo(A())选择非模板的原因 但是,在foo(B())的情况下,使用非模板需要派生到基的转换。因此函数模板严格来说是更好的,因此选择了它 foo模板实例化为void foo(const B&)。考虑一下没有模板的情况: Called A Called templ

我的印象是,总是会选择一个合适的非模板函数而不是模板函数。有人能给我解释一下导致这个令人惊讶的结果的解决步骤吗

我的印象是,总是会选择一个合适的非模板函数而不是模板函数

只有当模板和非模板都是同样好的候选对象时,这一点才成立。这就是为
foo(A())
选择非模板的原因

但是,在
foo(B())
的情况下,使用非模板需要派生到基的转换。因此函数模板严格来说是更好的,因此选择了它

foo
模板实例化为
void foo(const B&)
。考虑一下没有模板的情况:

Called A
Called template
voidfoo(constb&x){std::coutn337613.3.3.1/6

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

n3376 13.3.3.1/8

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

标识转换在13.3.3.1.1/表12中的表中具有精确的匹配等级,但派生到基的比标识更差

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

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

void foo(const A &a) { std::cout << "Called A" << std::endl; }
两者都有标识等级,但由于第一个是函数模板,所以将选择第二个。 在第二种情况下

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

// non-template
void foo(const A&)
只有第一个有身份等级,将被选中

有人能给我解释一下导致这个令人惊讶的结果的解决步骤吗

您可以在cppreference.com上查看过载分辨率:

具体请参见隐式转换序列的排序部分

延长答覆:

我试图通过上述链接中的信息摘录提供更多的澄清:

函数模板本身不是类型、函数或任何其他实体。不会从仅包含模板定义的源文件生成任何代码。为了显示任何代码,必须实例化模板:必须确定模板参数,以便编译器生成实际函数(或类,来自类模板)

为此,编译器将执行以下操作:

  • 函数模板名称查找
  • 模板参数推导
在这里,编译器有两个候选函数定义,它们可以处理特定的函数调用。这些候选函数是程序中模板函数的实例以及相关的非模板函数定义

但事实上,你的问题的答案就在这里:

模板参数推导发生在函数模板名称查找之后(可能涉及参数相关查找)和重载解析之前

函数重载解析是在模板函数实例化之后执行的,这是代码输出的原因

现在,您的具体案例将按照以下方式进行过载解决:

过载分辨率:

如果[previous]步骤生成多个候选函数,则会执行重载解析以选择实际将被调用的函数。通常,参数与参数最匹配的候选函数就是被调用的函数。 . .


如果F1的所有参数的隐式转换不比F2的所有参数的隐式转换差,则F1被确定为比F2更好的函数,
1) 至少有一个F1参数的隐式转换优于该F2参数的相应隐式转换
... .
.
.
隐式转换序列的排序:

每种类型的标准转换序列都分配了三个等级中的一个:
1) 精确匹配:无需转换、左值到右值转换、限定转换、类类型到同一类的用户定义转换
2) 升级:积分升级、浮点升级
3) 转换:整数转换、浮点转换、浮点积分转换、指针转换、指针到成员转换、布尔转换、用户定义的派生类到其基的转换

标准转换序列的秩是它所持有的标准转换秩中最差的一个(最多可能有三个转换)

直接将引用参数绑定到参数表达式是标识或派生到基的转换:

struct Base{};
派生结构:基{}d;
int f(Base&);//重载#1
int f(派生的&);//重载#2
int i=f(d);/d->派生并具有秩精确匹配
//d->Base&具有等级转换
//调用f(派生&)

好吧,这里有一个猜测:
B
继承自
a
,但它不是
a
。因此,当编译器执行重载解析时,它首先查找接受
B
void foo(const a&a)的函数
不符合此标准。但是,模板函数确实符合此标准,因为
t
可以是
B
。因此,
void foo(const t&x)
是首选。无论如何,这与
重载解析
标记描述完美匹配:“……它的规则非常复杂,甚至对有经验的用户来说也常常令人惊讶。”所以,So true=)有关详细信息,请参阅。我想知道的是,是否有一种方法可以编写类似于上述模板重载的重载,该重载只被选为绝对的最后一个恢复,一种回退。@emilerikson这是
// 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&)