重载解析算法中的歧义是如何确定的?
How is ambiguity determined in the overload resolution algorithm?
我正在尝试了解重载解析方法。
为什么会这样模棱两可:
void func(double, int, int, double) {}
void func(int, double, double, double) {}
void main()
{
func(1, 2, 3, 4);
}
但这不是吗?
void func(int, int, int, double) {}
void func(int, double, double, double) {}
void main()
{
func(1, 2, 3, 4);
}
在第一种情况下,有 2 次完全匹配的参数和 2 次转换对 1 次完全匹配和 3 次转换,在第二种情况下,有 3 次完全匹配和 1 次转换对 1 次完全匹配和 3 次转换。
那为什么一个有歧义,一个没有呢?这是什么逻辑?
重载解析规则仅定义所有匹配集的部分顺序 - 如果重载 F1
不是比 F2
更好的匹配,它并不意味着 F2
比 F1
更匹配。确切的偏序可以被认为是比较 k
维度中的两个点,其中参数的数量是 k
。让我们在 k
-dim space - (x_1, x_2,..., x_k) < (y_1, y_2,..., y_k) if x_i <= y_i for all i and x_j < y_j for at least one j
中的点上定义此偏序。这正是标准定义的候选非模板函数的偏序。
让我们看看你的例子:
void func(double, int, int, double) {}
vvv vvv vvv
better better equal
void func(int, double, double, double) {}
vvv vvv
better equal
所以这两个重载严格来说都不比另一个好。
在你的第二个例子中:
void func(int, int, int, double) {}
vvv vvv vvv vvv
equal better better equal
void func(int, double, double, double) {}
vvv
equal
现在,除了一个参数外,第一个重载比第二个好,而且永远不会比第二个差。因此,没有歧义 - 部分顺序确实声明第一个更好。
(以上描述未考虑函数模板,详情请见cppreference。)
标准 (§[over.match.best]/1) 中的措辞是:
[...] let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F.
[...] a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2)
在你的第一种情况下,这两个函数在第一次测试中失败。对于第一个参数,第一个函数(取 double
)的转换顺序比第二个差。对于第二个参数,第二个函数的转换顺序比第一个差(同样,在一种情况下 int
必须提升为 double
,而在另一种情况下则不需要)。
因此,两个函数都没有通过第一个规则,并且调用不明确。
在第二对函数之间,第一个函数的每个参数的转换至少与第二个函数的匹配参数一样好。然后我们继续第二条规则,发现至少有一个参数(事实上是两个)第一个函数比第二个函数具有更好的转换(身份而不是提升)。
因此,第一个功能比较匹配,将被选中。
歧义由排名决定:
- 精确匹配:不需要转换,左值到右值的转换,限定转换,class类型到相同class
的用户自定义转换
- 推广方式:积分推广、浮点推广
- 转换:整数转换、浮点数转换、浮点数转换、指针转换、指针到成员的转换、布尔值转换、派生class到其基数的用户定义转换
精确匹配胜过推广,胜过转化。
示例中:
void func(int, bool, float, int){cout << "int,bool,float,int" << endl;}
void func(int, bool, int, int){cout << "int,int,int,int" << endl;}
int main()
{
func(1,1,3.4,4);
}
参数 1(1
) 与两者完全匹配
参数 2(1
) 与
完全匹配
参数 3(3.4
) 可以转换为 float 和 int - 歧义 更好.
参数 4(4
) 与
完全匹配
但如果我们这样做:func(1,1,3.4f,4);
(3.4f
) 现在是完全匹配!
void func(int, bool, float, int)
然后赢得战斗。
我正在尝试了解重载解析方法。
为什么会这样模棱两可:
void func(double, int, int, double) {}
void func(int, double, double, double) {}
void main()
{
func(1, 2, 3, 4);
}
但这不是吗?
void func(int, int, int, double) {}
void func(int, double, double, double) {}
void main()
{
func(1, 2, 3, 4);
}
在第一种情况下,有 2 次完全匹配的参数和 2 次转换对 1 次完全匹配和 3 次转换,在第二种情况下,有 3 次完全匹配和 1 次转换对 1 次完全匹配和 3 次转换。
那为什么一个有歧义,一个没有呢?这是什么逻辑?
重载解析规则仅定义所有匹配集的部分顺序 - 如果重载 F1
不是比 F2
更好的匹配,它并不意味着 F2
比 F1
更匹配。确切的偏序可以被认为是比较 k
维度中的两个点,其中参数的数量是 k
。让我们在 k
-dim space - (x_1, x_2,..., x_k) < (y_1, y_2,..., y_k) if x_i <= y_i for all i and x_j < y_j for at least one j
中的点上定义此偏序。这正是标准定义的候选非模板函数的偏序。
让我们看看你的例子:
void func(double, int, int, double) {}
vvv vvv vvv
better better equal
void func(int, double, double, double) {}
vvv vvv
better equal
所以这两个重载严格来说都不比另一个好。
在你的第二个例子中:
void func(int, int, int, double) {}
vvv vvv vvv vvv
equal better better equal
void func(int, double, double, double) {}
vvv
equal
现在,除了一个参数外,第一个重载比第二个好,而且永远不会比第二个差。因此,没有歧义 - 部分顺序确实声明第一个更好。
(以上描述未考虑函数模板,详情请见cppreference。)
标准 (§[over.match.best]/1) 中的措辞是:
[...] let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F.
[...] a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2)
在你的第一种情况下,这两个函数在第一次测试中失败。对于第一个参数,第一个函数(取 double
)的转换顺序比第二个差。对于第二个参数,第二个函数的转换顺序比第一个差(同样,在一种情况下 int
必须提升为 double
,而在另一种情况下则不需要)。
因此,两个函数都没有通过第一个规则,并且调用不明确。
在第二对函数之间,第一个函数的每个参数的转换至少与第二个函数的匹配参数一样好。然后我们继续第二条规则,发现至少有一个参数(事实上是两个)第一个函数比第二个函数具有更好的转换(身份而不是提升)。
因此,第一个功能比较匹配,将被选中。
歧义由排名决定:
- 精确匹配:不需要转换,左值到右值的转换,限定转换,class类型到相同class 的用户自定义转换
- 推广方式:积分推广、浮点推广
- 转换:整数转换、浮点数转换、浮点数转换、指针转换、指针到成员的转换、布尔值转换、派生class到其基数的用户定义转换
精确匹配胜过推广,胜过转化。
示例中:
void func(int, bool, float, int){cout << "int,bool,float,int" << endl;}
void func(int, bool, int, int){cout << "int,int,int,int" << endl;}
int main()
{
func(1,1,3.4,4);
}
参数 1(1
) 与两者完全匹配
参数 2(1
) 与
完全匹配
参数 3(3.4
) 可以转换为 float 和 int - 歧义 更好.
参数 4(4
) 与
但如果我们这样做:func(1,1,3.4f,4);
(3.4f
) 现在是完全匹配!
void func(int, bool, float, int)
然后赢得战斗。