模板功能的重载解析

时间:2019-09-04 13:30:31

标签: c++ templates overload-resolution

考虑以下代码:

#include <iostream>

//Number1
template<typename T1, typename T2>
auto max (T1 a, T2 b)
{
    std::cout << "auto max(T1 a, T2 b)" <<std::endl;
    return  b < a ? a : b;
}

//Number2
template<typename RT, typename T1, typename T2>
RT max (T1 a, T2 b)
{
    std::cout << "RT max(T1 a, T2 b)" << std::endl;
    return  b < a ? a : b;
}


int main()
{
    auto a = ::max(4, 7.2);         //Select Number1

    auto b = ::max<double>(4, 7.4); //Select Number2

    auto c = ::max<int>(7, 4.);     //Compile-time error overload ambiguous

    auto c = ::max<double>(7, 4.); //Select Number2

}

auto c = ::max<int>(7, 4.);:该行由于过载含糊不清,并显示以下消息而无法编译:

maxdefault4.cpp:9:27: error: call of overloaded 'max(int, double)' is ambiguous
  auto c = ::max<int>(7, 4.);
                           ^
maxdefault4.cpp:9:27: note: candidates are:
In file included from maxdefault4.cpp:1:0:
maxdefault4.hpp:4:6: note: auto max(T1, T2) [with T1 = int; T2 = double]
 auto max (T1 a, T2 b)
      ^
maxdefault4.hpp:11:4: note: RT max(T1, T2) [with RT = int; T1 = int; T2 = double]
 RT max (T1 a, T2 b)
    ^

同时输入以下代码: àuto c = ::max<double>(7, 4.)成功了,为什么我们没有相同的错误消息,说明max<double>的呼叫与max<int>失败一样?

为什么double没问题?

我在《 C ++模板,完成指南》一书中读过,模板参数推导没有考虑返回类型,所以为什么max<int>是模棱两可而不是max<double>? >

在参数cutcution中是否确实不考虑模板函数的返回类型?

3 个答案:

答案 0 :(得分:15)

  

模板参数推导不考虑返回类型,

是的。 Template argument deduction是根据函数参数执行的。

  

那为什么max<int>是模棱两可而不是max<double>

给出::max<int>(7, 4.),对于第一个重载,将第一个模板参数T1指定为int,并将T2从第二个推导为double函数参数4.,则实例化为 double max(int, double) 。对于第二次重载,将第一个模板参数RT指定为int,从T1推导int作为7,推导T2double中的4.,则实例化为 int max(int, double) Overload resolution也不考虑返回类型,这两个重载都是完全匹配,然后是不明确的。

给出::max<double>(7, 4.),对于第一个重载,将第一个模板参数T1指定为double,并从{{推导得出T2double 1}},因此实例化将为 4. 。对于第二次重载,将第一个模板参数double max(double, double)指定为RT,从double推导T1作为int,推导7T2中的double,则实例化为 4. 。然后,第二重载在overload resolution中获胜,因为这是一个完全匹配,第一个重载需要从double max(int, double)int隐式转换代表第一个参数double

答案 1 :(得分:3)

对于每个函数调用,编译器都有2个函数可供选择,并选择最佳的一个。从RT之外的参数推导出未知的模板参数,这些参数必须明确指定并且不能推导。

auto a = ::max(4, 7.2);

由于未指定RT且无法推导,因此第二个重载不可用,因此将被忽略。选择第一个,并将类型推导为intdouble

auto b = ::max<double>(4, 7.4);
现在指定了

RT,因此编译器可以选择使用max<double,int,double>max<double, double>,第3个模板参数版本的参数类型与函数参数完全匹配,而第2个模板参数版本需要将int转换为double,以便选择3参数重载。

auto c = ::max<int>(7, 4.);
现在指定了

RT,因此编译器可以选择使用max<int,int,double>max<int, double>,两个函数的参数类型现在相同,因此编译器无法在它们之间进行选择。

答案 2 :(得分:2)

让我们看一下在重载解析期间将double作为参数对编译器的作用。

对于“ Number1” max模板,它指定第一个参数必须为double类型。尝试进行模板匹配时,编译器推断出第二个参数的类型为double。因此,结果签名为auto max(double, double)。这是一个匹配,尽管它涉及将第一个参数从int强制转换为double

对于“ Number2” max模板,它指定返回类型为double。推导参数类型。因此,结果签名为double max(int, double)。完全匹配,消除了任何歧义。

现在让我们看一下指定int。现在,两个签名为auto max(int, double)double max(int, double)。如您所见,与重载解析没有任何区别,从而导致歧义。

从本质上讲,通过传入double,您通过强制进行不必要的转换而毒害了其中的一种重载。另一个过载由此占据主导地位。与此相反,传递int并不会进一步限制这两种过载都可以完美匹配。