请考虑以下代码:
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;
c.f();
return 0;
}
我恳请您不要复制粘贴有关如何解决钻石问题的标准回复("使用虚拟继承")。我在这里要问的是为什么在这种情况下使用声明不起作用。确切的编译器错误为:
In function 'int main()':
prog.cpp:31:6: error: 'A' is an ambiguous base of 'C'
c.f();
我的印象是使用声明应该从这个例子中工作:
struct A
{
void f()
{
}
};
struct B
{
void f()
{
}
};
struct C : A, B
{
using A::f;
};
int main()
{
C c;
c.f(); // will call A::f
return 0;
}
其他人可以找到标准报价,但我将在概念上解释。
它不起作用,因为使用声明仅影响名称查找。
您的using-声明会导致名称查找成功,否则它会失败,也就是说,它告诉编译器在哪里可以找到函数f
。但它并没有告诉它f
作用于哪个子对象A
,也就是说,在调用f
时,哪个子对象将作为隐式this
参数传递。
即使有两个C
的A
子对象,也只有一个函数A::f
,并且它需要一个隐式的this
参数类型A*
。为了在C
对象上调用它,必须将C*
隐式转换为A*
。这始终是模棱两可的,并且不受任何使用声明的影响。
(如果将数据成员放在A
中,这更有意义。然后C
每个这样的数据成员中有两个。调用f
时,如果它访问数据成员,它是访问从B1
继承的A
子对象中的成员,还是访问从B2
继承的A
子对象中的子对象?
[namespace.udecl]/p17 中有一个注释直接解决了这种情况:
[注意:因为using-声明指定基类成员 (而不是基类的成员子对象或成员函数 子对象),使用声明不能用于解析继承的 成员歧义。例如
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 }
—尾注]
除了 T.C. 的回答之外,我还想补充一点,派生类中的名称查找在标准中在 10.2 节中进行了非常详细的解释。
这里说的是关于使用声明的处理:
10.2/3:查找集 (...) 由两个组件集组成:声明集,一组名为 f 的成员;子对象集,一组子对象,其中这些成员的声明(可能包括 使用声明)被发现。在声明集中,use-声明由它们指定的成员替换,并且键入声明(包括 注入的类名)将替换为它们指定的类型。
因此,当您尝试在struct C
中声明时
using B1::f; // you hope to make clear that B1::f is to be used
根据查找规则,编译器仍然会找到可能的候选项:B1::f
和B2::f
,因此它仍然不明确。