C ++模板化函数重载规则

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

我可以在自己的代码中解决此要求,并且此示例是我尝试做的简化版本,但我认为这是相同的问题。

Bar*_*rry 5

重载解析的规则是这样的:

  1. 按名称查找所有候选函数
  2. 执行模板推导并修剪到可行的候选者(即丢弃格式错误的调用)。
  3. 通过以下方式选择最佳可行候选人:

    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

您可以将 强制转换ChildParent自己:

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 )