为什么没有使用申报工作来解决钻石问题呢?
请考虑下面的代码:
struct A { void f() { } }; struct B1 : A { }; struct B2 : A { }; struct C : B1, B2 { void f() // works { B1::f(); } //using B1::f; // does not work //using B1::A::f; // does not work as well }; int main() { C c; cf(); return 0; }
我恳请您不要复制粘贴关于如何解决钻石问题(“使用虚拟inheritance”)的标准答复。 我在这里问的是为什么在这种情况下不使用声明。 确切的编译器错误是:
In function 'int main()': prog.cpp:31:6: error: 'A' is an ambiguous base of 'C' cf();
我得到了一个使用声明应该从这个例子中工作的印象:
struct A { void f() { } }; struct B { void f() { } }; struct C : A, B { using A::f; }; int main() { C c; cf(); // will call A::f return 0; }
其他人可以find标准的引用,但是我要在概念上解释。
它不起作用,因为使用声明 只影响名称查找。
您的使用声明会导致名称查找成功,否则它会失败,也就是说,它会告诉编译器在哪里find函数f
。 但是它并不告诉它哪个子对象f
作用在哪个子对象上 ,也就是说,当调用f
时,哪个子对象将作为隐含的parameter passing。
尽pipe有两个C
A
子对象,但是只有一个函数A::f
,并且需要一个隐含的typesA*
参数。 为了在C
对象上调用它,必须将C*
隐式转换为A*
。 这总是不明确的,不受任何使用声明的影响 。
(如果把数据成员放到A
里面,这样做更有意义,那么C
就会有两个这样的数据成员,当调用f
,如果访问数据成员,它是否访问从B1
inheritance的A
子对象中的那个,或者A
子对象中的inheritance自B2
?)
[namespace.udecl] / p17中有一个说明直接解决了这种情况:
[ 注意 :由于using-declaration指定基类成员(而不是成员子对象或基类子对象的成员函数),因此不能使用using-declaration来parsinginheritance的成员歧义。 例如,
struct A { int x(); }; struct B : A { }; struct C : A { using A::x; int x(int); }; struct D : B, C { using C::x; int x(double); }; int f(D* d) { return d->x(); // ambiguous: B::x or C::x }
– 结束注意 ]
除了TC的回答之外,我想补充一点,派生类中的名称查找在10.2节中有详细的解释。
这里关于处理使用声明的内容如下:
10.2 / 3:查找集(…)由两个组件集组成:声明集,一组名为f的成员; 和subobject集合,一组子对象,这些成员的声明(可能包括使用声明)被发现。 在声明集中, 使用声明被他们指定的成员replace ,并且types声明(包括注入类名称)被他们指定的typesreplace。
所以当你尝试在struct C
声明的时候
using B1::f; // you hope to make clear that B1::f is to be used
根据查找规则,编译器会find可能的候选项: B1::f
和B2::f
这样它们仍然是不明确的。