C ++模板函数重载规则 [英] C++ templated function overloading rules

查看:89
本文介绍了C ++模板函数重载规则的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当重载模板函数时,如果它有以下选项,编译器应该如何选择调用哪个版本的函数:




  • 调用函数的模板版本(例如 func< T>(foo))。

  • 函数本身不是模板,但是传递给函数的参数的类型从重载函数模板中指定的类型继承。



考虑以下C ++代码:

  #include< stdio.h> 

struct Parent {};
struct Child:public Parent {};

template< typename T>
void func(T){
printf(func(T)\\\
);
}

void func(Parent){
printf(func(Parent)\\\
);
}

int main(){
func(1);
func(Parent());
func(Child());
}

编译gcc或clang,输出:

  func(T)
func(Parent)
func(T)
pre>

前两行是预期和有意义的。然而,在调用 func(Child()),它可以很容易地调用 func(Parent)



因此,我有两个主要问题:




  • 标准规定的关于如何解决这些冲突的具体规则是什么?此此问题/答案中有一些信息,但

  • 是否有任何方式强制编译器调用 func(Parent) a Child



我可以在自己的代码中解决这个要求,这个例子是我想要做的简化版本,但我相信它是同样的问题。

解决方案

重载解析的规则如下:


  1. 按名称查找所有候选函数

  2. 执行

  3. 通过


    选择最佳的可行候选人

    a。选择具有最佳转换顺序的那个(认为这是从参数类型转换为参数类型所做的最少必要的工作)

    b。在函数模板上选择非功能模板

    c。选择最专业的函数模板


让我们逐一介绍一下。对于您的函数调用:

  func(1) 

在(2)之后,我们有一个可行的候选, func< int& 无法构建 int func(Parent) / code>,所以我们完成并调用函数模板。

  func 

我们有两个可行的候选者: func< Parent> func(Parent)。两者采用完全相同的参数,因此转换序列是相同的。所以我们最终在步骤3b:我们选择非模板在模板上,我们调用 func(Parent)

  func(Child()); 

我们有两个可行的候选者: func< Child> func(Parent)。在前一种情况下,参数类型是 Child ,因此对于我们传入的内容,它是一个完全匹配(不需要转换)。在后一种情况下,参数类型为 Parent ,因此我们必须执行派生到基础的转换。由于函数模板具有更好的转换序列(即,没有必要的转换),它被认为是最好的可行的过载。您可以 调用 func(Parent) - 这是一个可行的候选人,但不是 / em>可行候选人。 func< Child> 是更好的匹配。


有任何方法强制编译器调用 func(Parent)传递子?


>

  >儿童c; 
func(static_cast< Parent>(c));

或者你可以写另一个重载,需要一个 Child ,这仅在第三种情况下是首选(只在第三种情况下可行):

  void func ); 

或重写函数模板,以便不在该层次结构中包含任何类:

 模板< typename T,
typename = std :: enable_if_t<
!std :: is_convertible< T *,Parent *> :: value
>>
void func(T);

后面的解决方案(称为SFINAE)将删除 func< Child> code>,因此唯一可行的候选成为 func(Parent)


When overloading a templated function, how should the compiler chose which version of the function to call if it has the option to either:

  • Call a templated version of the function (such as func<T>(foo)).
  • Call an overloaded version of the function which is not itself templated but where the type of the parameter being passed to the function inherits from the type specified in the overloaded function template.

Consider the following C++ code:

#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());
}

Compiled with gcc or clang, this outputs:

func(T)
func(Parent)
func(T)

The first two lines are expected and make sense. However, in the call func(Child()), it could just as easily call func(Parent) (which seems like, if anything, what it should do).

As such, I have two main questions:

  • What are the exact rules laid out by the standard as to how to resolve such conflicts? There is some information in this question/answer, but if anything it conflicts with what I am observing.
  • Is there any way to force the compiler to call func(Parent) when passed a Child?

I can get around this requirement in my own code and this example is a simplified version of what I am trying to do, but I believe that it is the same problem.

解决方案

The rules for overload resolution go something like this:

  1. Find all the candidate functions by name
  2. Perform template deduction and prune down to the viable candidates (i.e. drop the calls that are ill-formed).
  3. Pick the best viable candidate via:

    a. Choose the one with the best conversion sequence (think of this as "doing the least necessary work to convert from the argument types to the parameter types")
    b. Choose the non-function template over the function template
    c. Choose the most specialized function template

Let's go into these on a case by case basis. For your function calls:

func(1);

After (2), we have one viable candidate, func<int>. func(Parent ) is not a viable candidate, since Parent is not constructible from int, so we're done and call the function template.

func(Parent());

We have two viable candidates: func<Parent> and func(Parent ). Both take the exact same arguments so the conversion sequences are identical. So we end up in step 3b: we choose the non-template over the template, and we call func(Parent ).

func(Child());

We have two viable candidates: func<Child> and func(Parent ). In the former case, the argument type is Child so it's an exact match for what we're passing in (no conversion necessary). In the latter case, the argument type is Parent so we'd have to perform a derived-to-base conversion. Since the function template has a better conversion sequence (i.e. no conversion necessary), it is considered the best viable overload. You could call func(Parent ) - that is a viable candidate, but it's not the best viable candidate. func<Child> is a better match.

Is there any way to force the compiler to call func(Parent) when passed a Child?

You could either cast the Child to a Parent yourself:

Child c;
func(static_cast<Parent>(c));

Or you could write another overload that takes a Child, which would be preferred only in the 3rd case (and only viable in the 3rd case):

void func(Child );

Or rewrite your function template so as to not take any class in that hierarchy:

template <typename T,
          typename = std::enable_if_t<
              !std::is_convertible<T*, Parent*>::value
          >>
void func(T );

The latter solution (called SFINAE) would remove func<Child> from the set of viable candidates, so that the only viable candidate becomes func(Parent ).

这篇关于C ++模板函数重载规则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆