为什么没有使用申报工作来解决钻石问题呢?

请考虑下面的代码:

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 ,如果访问数据成员,它是否访问从B1inheritance的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::fB2::f这样它们仍然是不明确的。