模板函数的重载解析
Overload resolution of template functions
考虑这段代码:
#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++ templates, the completes Guide" 书中读到模板参数推导没有考虑 return 类型,所以为什么 max<int>
不明确而不是 max<double>
?
return 类型的模板函数真的没有考虑到参数推导吗?
the template argument deduction does not take the return type into account,
是的。 Template argument deduction 根据函数参数执行。
so why max<int>
is ambiguous and not max<double>
?
给定 ::max<int>(7, 4.)
,对于第一个重载,第一个模板参数 T1
被指定为 int
,并且 T2
被推导出为 double
第二个函数参数 4.
,则实例化将是 double max(int, double)
。对于第二次重载,第一个模板参数 RT
指定为 int
,T1
从 7
推导为 int
,推导 T2
作为 double
来自 4.
,那么实例化将是 int max(int, double)
。 Overload resolution 也没有考虑 return 类型,两个重载都是 完全匹配 然后歧义
给定 ::max<double>(7, 4.)
,对于第一个重载,第一个模板参数 T1
指定为 double
,并且 T2
推导为 double
来自4.
,所以实例化将是 double max(double, double)
。对于第二次重载,第一个模板参数 RT
指定为 double
,T1
从 7
推导为 int
,推导 T2
作为 4.
的 double
,那么实例化将是 double max(int, double)
。然后第二个重载在 overload resolution 中获胜,因为它是 完全匹配 ,第一个重载需要从 int
进行 隐式转换 到 double
第一个参数 7
.
让我们看看在重载解析期间将 double
指定为参数对编译器有何影响。
对于"Number1"max
模板,它指定第一个参数必须是double
类型。尝试进行模板匹配时,编译器推断第二个参数的类型为 double
。所以生成的签名是auto max(double, double)
。这是一场比赛,尽管它涉及将第一个参数从 int
转换为 double
.
对于"Number2"max
模板,指定return类型为double
。推导参数类型。所以生成的签名是double max(int, double)
。这是一个完全匹配,消除了任何歧义。
现在让我们看看指定 int
。现在这两个签名是 auto max(int, double)
和 double max(int, double)
。如您所见,与重载决议相关的差异没有,导致歧义。
本质上,通过传入 double
,您通过强制不必要的转换毒化了其中一个重载;另一个过载从而占据主导地位。相反,传入 int
不会进一步限制任一重载成为完美匹配的能力。
对于您的每个函数调用,编译器都有 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>
,两个函数的参数类型现在相同,因此编译器无法在两者之间进行选择他们。
考虑这段代码:
#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++ templates, the completes Guide" 书中读到模板参数推导没有考虑 return 类型,所以为什么 max<int>
不明确而不是 max<double>
?
return 类型的模板函数真的没有考虑到参数推导吗?
the template argument deduction does not take the return type into account,
是的。 Template argument deduction 根据函数参数执行。
so why
max<int>
is ambiguous and notmax<double>
?
给定 ::max<int>(7, 4.)
,对于第一个重载,第一个模板参数 T1
被指定为 int
,并且 T2
被推导出为 double
第二个函数参数 4.
,则实例化将是 double max(int, double)
。对于第二次重载,第一个模板参数 RT
指定为 int
,T1
从 7
推导为 int
,推导 T2
作为 double
来自 4.
,那么实例化将是 int max(int, double)
。 Overload resolution 也没有考虑 return 类型,两个重载都是 完全匹配 然后歧义
给定 ::max<double>(7, 4.)
,对于第一个重载,第一个模板参数 T1
指定为 double
,并且 T2
推导为 double
来自4.
,所以实例化将是 double max(double, double)
。对于第二次重载,第一个模板参数 RT
指定为 double
,T1
从 7
推导为 int
,推导 T2
作为 4.
的 double
,那么实例化将是 double max(int, double)
。然后第二个重载在 overload resolution 中获胜,因为它是 完全匹配 ,第一个重载需要从 int
进行 隐式转换 到 double
第一个参数 7
.
让我们看看在重载解析期间将 double
指定为参数对编译器有何影响。
对于"Number1"max
模板,它指定第一个参数必须是double
类型。尝试进行模板匹配时,编译器推断第二个参数的类型为 double
。所以生成的签名是auto max(double, double)
。这是一场比赛,尽管它涉及将第一个参数从 int
转换为 double
.
对于"Number2"max
模板,指定return类型为double
。推导参数类型。所以生成的签名是double max(int, double)
。这是一个完全匹配,消除了任何歧义。
现在让我们看看指定 int
。现在这两个签名是 auto max(int, double)
和 double max(int, double)
。如您所见,与重载决议相关的差异没有,导致歧义。
本质上,通过传入 double
,您通过强制不必要的转换毒化了其中一个重载;另一个过载从而占据主导地位。相反,传入 int
不会进一步限制任一重载成为完美匹配的能力。
对于您的每个函数调用,编译器都有 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>
,两个函数的参数类型现在相同,因此编译器无法在两者之间进行选择他们。