二义性调用(Ambiguous Call)

一天早上遇到的事物,下午又恰好看到了专业的补充,对此将其记录下来。


二义性调用(Ambiguous Call),属于一种编译错误。

在调用函数,去匹配合适的参数列表时,发现当前所提供参数,能匹配出多个重载函数。比如:

int add(int a, int b) {
    
      
    return a + b;  
}  
  
template<typename T>  
T add(T a, T b) {
    
      
    return a + b;  
}  
  
int main() {
    
      
    add(10, 20);  
    add(10.5, 20.5);  
    add(string("10.5"), string("20.5"));
    return 0;
}

当然,编译器不会那么呆,会追求最佳匹配。当参数为两个 int 时,就会调用 add(int, int), 而不会是 add(T, T)

但是,若最佳匹配也存在多个的情况下,那么就不对劲了:

long long add(int, long long) {
    
      
    return 0;  
}  
  
long long add(long long, int) {
    
      
    return 0;  
}  
  
int main() {
    
      
    // ???
    add(10, 20);  
    return 0;  
}

对于字面量整数来说,完全兼容 intlong long 类型,于是就无法区分,到底要调用是哪一个函数,因为无论调用哪一个,都合情合理,从类型的角度来说完全没有问题。

对于 add(int, long long)add(long long, int) 所形成的的重构有点扯,但此处只是演示二义性问题,不探究严谨性。

最佳解决方案是:完全杜绝这种函数重构的产生。

否则,请在调用时,显式告诉编译器,该字面量值的类型具体是什么,而不要让其自行推测。比如:

long long add(int, long long) {
    
      
    return 0;  
}  
  
long long add(long long, int) {
    
      
    return 0;  
}  
  
int main() {
    
      
    // ok
    int a = 10;  
    long long b = 20;  
    add(a, b);  
    add((long long)10, 20);  
    add(10, (long long)20);  
    return 0;  
}

其中发现,若是只标注任意一参数类型为 int,依然会导致二义性调用,比较有趣,可以分析一下为什么。

int main() {
    
      
    // ???
    add((int)10, 20);  
    add(10, (int)20);  
    return 0;  
}

若是显式说明其中一个参数为 int,那么根据我们的观察,应该去选择函数签名为 int a, T bT a, int b 的函数,来达成匹配。

然而对于整数字面量来说,一个默认行为就是,将其当作 int 进行处理,于是又回到了原点,int 都能无损转换为 long long,那谁来转换。显式标注并不能参与转换优先级竞争

只有当另一个未显式指定 int 类型的参数,其数值超过了 int 范围,那么才会将其定位至 long long, 从而达成匹配。


正常情况下,是很难遇到二义性调用的,想要随手写出这种函数都很难,知道这样一个术语,用于描述这种状况即可。

End

猜你喜欢

转载自blog.csdn.net/qq_49661519/article/details/125249427