模板函数的重载解析



请考虑以下代码:

#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>呢?

在参数 dedcution 中真的不考虑模板函数的返回类型吗?

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

是的。模板参数推导基于函数参数执行。

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

给定::max<int>(7, 4.),对于第一个重载,第一个模板参数T1被指定为int,并且T2从第二个函数参数4.推导出为double,则实例化将是double max(int, double)。对于第 2 个重载,第一个模板参数RT被指定为intT17推导出为intT24.推导为double,则实例化将被int max(int, double)。重载解析也不考虑返回类型,这两个重载既完全匹配,又不明确。

给定::max<double>(7, 4.),对于第一个重载,第一个模板参数T1被指定为double,并且T2被推导出为从4.double,所以实例化将是double max(double, double)。对于第 2 个重载,第一个模板参数RT被指定为doubleT17推导为intT24.推导为double,则实例化将被double max(int, double)。然后第二个重载在重载分辨率中获胜,因为它是完全匹配的,第一个重载需要第一个参数7int式转换为double

对于每个函数调用,编译器都有 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>,这两个函数的参数类型现在相同,因此编译器无法在它们之间进行选择。

让我们看看在重载解析期间将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并不能进一步限制任何一个过载成为完美匹配的能力。

相关内容

  • 没有找到相关文章

最新更新