提问人:imbot 提问时间:10/27/2023 最后编辑:YSCimbot 更新时间:10/27/2023 访问量:106
过载解决方法中的混淆
Confusion in Overload Resolution
问:
我无法弄清楚过载解决方案在这里是如何工作的!对于第一个代码段,它解析为 OVERLOAD A,但对于第二个代码段,它会引发错误“模棱两可的调用”。
代码 1
#include <iostream>
using namespace std;
void doThings_A(int ,int ,double){ cout<<"first fxn"<<endl;} //OVERLOAD A
void doThings_A(int ,double,double){ cout<<"second fxn"<<endl;} //OVERLOAD B
int main() {
// Write C code here
doThings_A(4,5,6);
return 0;
}
代码 1 的输出:第一个 FXN
代码 2
#include <iostream>
using namespace std;
void doThings_A(double ,int ,int){ cout<<"first fxn"<<endl;}
void doThings_A(int ,double,double){ cout<<"second fxn"<<endl;}
int main() {
// Write C code here
doThings_A(4,5,6);
return 0;
}
代码 2 的输出:
error: call of overloaded 'doThings_A(int, int, int)' is ambiguous
8 | doThings_A(4,5,6);
| ~~~~~~~~~~^~~~~~~
/tmp/sI0f5j5PL7.cpp:4:6: note: candidate: 'void doThings_A(double, int, int)'
4 | void doThings_A(double ,int ,int){ cout<<"first fxn"<<endl;}
| ^~~~~~~~~~
/tmp/sI0f5j5PL7.cpp:5:6: note: candidate: 'void doThings_A(int, double, double)'
5 | void doThings_A(int ,double,double){ cout<<"second fxn"<<endl;}
答:
在这方面,C++ 设计的一般原则(无论是选择重载还是模板专用化、比较强度等)是它不会将“分数”分配给不同的替代方案(基于需要隐式转换的参数数量,如果我们谈论的是函数重载或其他任何东西)。concept
相反,要使 A 比 B 更好,A 必须在被比较的每个方面都不差,而且至少在一个方面也更好。
对于函数重载,要比较的“方面”是每个单独的参数是否需要隐式转换:
在第一个代码段中,两个重载相对于参数 1 和 3 是等效的(即,两者都不比另一个差),但第一个重载相对于参数 2 更好。因此,第一次重载总体上更好。
在第二个代码段中,第一个重载相对于参数 2 和 3 更好,但第二个重载相对于参数 1 更好,因此存在歧义。
当然,它还有更多细微差别,但一般原则仍然成立:
这里的决定因素是(过载分辨率 - 最佳可行函数):
如果 F1 的所有参数的隐式转换不比 F2 的所有参数的隐式转换差,则 F1 被确定为比 F2 更好的函数,并且
- F1 至少有一个参数的隐式转换优于 F2 参数的相应隐式转换
因此,让我们看一下签名:
void doThings_A(int ,int ,double) // 1
void doThings_A(int ,double,double) // 2
当用三个 s 调用时,第一个和第三个参数分别对 1 和 2 匹配和浮点转换同样有效。int
但在第二个参数中,1 是匹配项,而 2 需要浮点转换。这意味着每个参数的 1 等于或优于 2,因此选择了 1。
但是,在第二个示例中,我们有:
void doThings_A(double ,int ,int) // 3
void doThings_A(int ,double,double) // 4
这里 3 更适合第二个和第三个参数,而 4 更适合第一个参数。这意味着没有明确更好的候选者,因此解决方案失败。
在这两种情况下,两个重载都是调用的可行函数,但在第一种情况下,一个是比另一个“更好的函数”。doThings_A
doThings_A(4,5,6)
F1
被确定为一个更好的函数,如果 的所有参数的隐式转换不比 的所有参数的隐式转换差,并且F2
F1
F2
- 至少有一个参数的隐式转换优于该参数的相应隐式转换
F1
F2
在这两种情况下,最糟糕的隐式转换 ( to ) 是相同的,因此必须考虑该子句。让我们逐个参数比较案例:int
double
1.
CASE 1
| arg1 | arg2 | arg3 |
CALL | int | int | int |
F1 | int (exact match) | int (exact match) | double (conversion) |
F2 | int (exact match) | double (conversion) | double (conversion) |
WINS | tie | F1 | tie |
至少有一个参数 () 的隐式转换更好,获胜并且是最佳的可行函数。arg2
F1
CASE 2
| arg1 | arg2 | arg3 |
CALL | int | int | int |
F1 | double (conversion) | int (exact match) | int (exact match) |
F2 | int (exact match) | double (conversion) | double (conversion) |
WINS | F2 | F1 | F1 |
根据相同的规则,两者都是比 ( 和 ) 更好的可行函数,并且是比 () 更好的可行函数。这导致两个函数的排名相等,因此出现不明确的调用错误。F1
F2
arg2
arg3
F2
F1
arg1
重载解析通过比较调用的每个参数与候选函数的相应参数的匹配情况,对可行的候选函数进行排名。更重要的是,要使一个候选者被认为优于另一个候选者,更好的候选者不能使其任何参数比另一个候选者中的相应参数更差。
现在,让我们将其应用于每个示例代码段,看看发生了什么。
片段 1
void doThings_A(int a,int b,double c){ cout<<"first fxn"<<endl;} //OVERLOAD A
void doThings_A(int a,double b,double c){ cout<<"second fxn"<<endl;} //OVERLOAD B
int main() {
// Write C code here
doThings_A(4,5,6);
return 0;
}
现在对于第一个重载,调用具有前两个参数,并且与相应的前两个参数(和分别)完全匹配,而第三个参数需要转换为第二个重载的第三个参数也需要转换为该参数。换言之,第一个候选者没有任何参数比第二个候选者中的相应参数更差的匹配度。doThings_A(int ,int ,double)
doThings(4,5,6)
4
5
a
b
6
double
另一方面,第二个重载的第二个参数比第一个重载的第二个参数的相应第二个参数的匹配度更差。因此,第二个重载不可能比第一个候选者更好。void doThings_A(int ,double,double)
void doThings_A(int ,int ,double)
总之,第一个候选人比第二个候选人更匹配。
片段 2
void doThings_A(double ,int ,int){ cout<<"first fxn"<<endl;}
void doThings_A(int ,double,double){ cout<<"second fxn"<<endl;}
int main() {
// Write C code here
doThings_A(4,5,6);
return 0;
}
在这种情况下,第一个重载的第一个参数比第二个重载的相应第一个参数更差匹配,因此第一个重载不能像开头突出显示的部分那样是更好的候选者。void doThings_A(double ,int ,int)
void doThings_A(int a,double b,double c)
同样,第二个重载的第二个和第三个参数比第一个重载的相应第二个和第三个参数的匹配度更差。因此,第二个候选人不可能比第一个候选人更好。void doThings_A(int a,double a,double a)
void doThings_A(double ,int ,int)
因此,在这种情况下,两者都不比其他更好,并且调用是模棱两可的。
评论