在《Effective C++》中有一篇《当心潜在的二义性》。
这是潜在二义性的一个例子:
class B; // 对类B提前声明
//
class A {
public:
A(const B&); // 可以从B构造而来的类A
};
class B {
public:
operator A() const; // 可以从A转换而来的类B
};
这些类的声明没一点错——他们可以在相同的程序中共存而没一点问题。但是,看看下面,当把这两个类结合起来使用,在一个输入参数为A的函数里实际传进了一个B的对象,这时将会发生什么呢?
void f(const A&);
B b;
f(b); // 错误!——二义
一看到对f的调用,编译器就知道它必须产生一个类型A的对象,即使它手上拿着的是一个类型B的对象。有两种都很好的方法来实现(见条款M5)。一种方法是调用类A的构造函数,它以b为参数构造一个新的A的对象。另一种方法是调用类B里自定义的转换运算符,它将b转换成一个A的对象。因为这两个途径都一样可行,编译器拒绝从他们中选择一个。
但是我在g++下编了如下的程序:
class B;
class A
{
public:
A(const B &b){cout << "B to A" << endl;}
//A(const A &a);
};
class B
{
public:
operator A()const{cout << "BB to A" << endl;}
};
void f(const A &a)
{
cout << "f() in" << endl;
}
int main()
{
B b = B();
//A a = b;
f(b);
}
居然能通过。输出为:
看到这里, 就有点奇怪了。这么g++不产生歧义啊。
我又修改了一下。如下:
看出来了吗?就是把B::operator A()const 后面的const去掉了。结果是:
很奇怪的结果。为什么呢?
对于第二种情况,我想应该是精确匹配起作用。因为b是一个非const的B,通过精确匹配掉用了B::operator A()。
对于第一种,我就有点像不明白了。A::A(const B &b)和B::operator A()const 就有同样的参数匹配等级。不知道。。。。。
带着这种疑惑,我把上面的程序用vs2008编译了一下,哈哈。。没通过,看了一下编译结果:
应该就是解析时产生了歧义吧。
看来vs2008在这方面做的还是不错。