考虑以下代码:
#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中是否确实不考虑模板函数的返回类型?
答案 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
,推导T2
如double
中的4.
,则实例化为 int max(int, double)
。 Overload resolution也不考虑返回类型,这两个重载都是完全匹配,然后是不明确的。
给出::max<double>(7, 4.)
,对于第一个重载,将第一个模板参数T1
指定为double
,并从{{推导得出T2
为double
1}},因此实例化将为 4.
。对于第二次重载,将第一个模板参数double max(double, double)
指定为RT
,从double
推导T1
作为int
,推导7
如T2
中的double
,则实例化为 4.
。然后,第二重载在overload resolution中获胜,因为这是一个完全匹配,第一个重载需要从double max(int, double)
到int
的隐式转换代表第一个参数double
。
答案 1 :(得分:3)
对于每个函数调用,编译器都有2个函数可供选择,并选择最佳的一个。从RT
之外的参数推导出未知的模板参数,这些参数必须明确指定并且不能推导。
auto a = ::max(4, 7.2);
由于未指定RT
且无法推导,因此第二个重载不可用,因此将被忽略。选择第一个,并将类型推导为int
和double
。
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
并不会进一步限制这两种过载都可以完美匹配。