for*_*rul 5 c++ templates overloading overload-resolution
重载模板化函数时,如果编译器可以选择以下任一选项,则编译器应如何选择要调用的版本:
func<T>(foo))。考虑以下C ++代码:
#include <stdio.h>
struct Parent {};
struct Child : public Parent {};
template <typename T>
void func(T) {
printf("func(T)\n");
}
void func(Parent) {
printf("func(Parent)\n");
}
int main() {
func(1);
func(Parent());
func(Child());
}
Run Code Online (Sandbox Code Playgroud)
用gcc或clang编译,输出:
func(T)
func(Parent)
func(T)
Run Code Online (Sandbox Code Playgroud)
前两行是预期的并且有意义。但是,在call中func(Child()),它可以像调用一样容易func(Parent)(这似乎(如果有的话)应该执行的操作)。
因此,我有两个主要问题:
func(Parent)在传递a时调用Child?我可以在自己的代码中解决此要求,并且此示例是我尝试做的简化版本,但我认为这是相同的问题。
重载解析的规则是这样的:
通过以下方式选择最佳可行候选人:
A。选择具有最佳转换顺序的一个(将其视为“从参数类型转换为参数类型所需要做的最少工作”)
b.选择非功能模板而不是功能模板
c. 选择最专业的功能模板
让我们根据具体情况来讨论这些问题。对于您的函数调用:
func(1);
Run Code Online (Sandbox Code Playgroud)
(2) 之后,我们就有了一个可行的候选者,func<int>。func(Parent )不是一个可行的候选者,因为Parent不能从 构造int,所以我们完成并调用函数模板。
func(Parent());
Run Code Online (Sandbox Code Playgroud)
我们有两个可行的候选者:func<Parent>和func(Parent )。两者都采用完全相同的参数,因此转换序列是相同的。所以我们最终进入步骤 3b:我们选择非模板而不是模板,然后调用func(Parent ).
func(Child());
Run Code Online (Sandbox Code Playgroud)
我们有两个可行的候选者:func<Child>和func(Parent )。在前一种情况下,参数类型与我们传入的内容完全匹配Child(无需转换)。在后一种情况下,参数类型是这样的,因此我们必须执行派生到基数的转换。由于函数模板具有更好的转换顺序(即无需转换),因此它被认为是最佳可行的重载。你可以打电话给- 这是一个可行的候选人,但它不是最好的可行候选人。是一个更好的匹配。Parentfunc(Parent )func<Child>
有什么方法可以强制编译器
func(Parent)在传递 a 时调用Child?
您可以将 强制转换Child为Parent自己:
Child c;
func(static_cast<Parent>(c));
Run Code Online (Sandbox Code Playgroud)
或者您可以编写另一个采用 a 的重载Child,这仅在第三种情况下才是首选(并且仅在第三种情况下可行):
void func(Child );
Run Code Online (Sandbox Code Playgroud)
或者重写您的函数模板,以便不采用该层次结构中的任何类:
template <typename T,
typename = std::enable_if_t<
!std::is_convertible<T*, Parent*>::value
>>
void func(T );
Run Code Online (Sandbox Code Playgroud)
后一种解决方案(称为 SFINAE)将从func<Child>可行候选集中删除,以便唯一可行的候选成为func(Parent )。