提问人:gd1 提问时间:5/5/2015 最后编辑:Cœurgd1 更新时间:11/9/2018 访问量:3494
为什么使用声明不能解决钻石问题?
Why doesn't a using-declaration work to solve the diamond problem?
问:
请考虑以下代码:
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();
从这个例子中,我得到的印象是 using 声明应该起作用:
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;
}
答:
[namespace.udecl]/p17 中有一个注释直接解决了这种情况:
[ 注意:因为 using 声明指定基类成员 (而不是基类的成员子对象或成员函数 subobject),using-declaration 不能用于解析继承的 成员歧义。例如
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 }
——尾注 ]
评论
A::x()
C
B
A
x()
其他人可以找到标准报价,但我将从概念上解释。
它不起作用,因为 using 声明只影响名称查找。
您的 using 声明会导致名称查找成功,否则它会失败,也就是说,它告诉编译器在哪里可以找到函数 f
。但它并没有告诉它哪个子对象
f
作用于哪个子对象,也就是说,哪个子对象在被调用时将作为隐式参数传递。this
f
即使有两个子对象,也只有一个函数,并且它接受一个 类型的隐式参数。为了在对象上调用它,必须隐式转换为 。这始终是模棱两可的,并且不受任何使用声明的影响。A::f
A
C
this
A*
C
C*
A*
(如果将数据成员放在 中,则更有意义。然后每个这样的数据成员将有两个。调用时,如果它访问数据成员,它是访问继承自的子对象中的子对象,还是继承自的子对象中的子对象?A
C
f
A
B1
A
B2
评论
除了 T.C. 的回答之外,我还想补充一点,派生类中的名称查找在第 10.2 节中对标准进行了非常详细的解释。
以下是关于处理使用声明的内容:
10.2/3:查找集 (...) 由两个组件集组成:声明集,一组名为 f 的成员;和子对象集,一组子对象,其中声明这些成员(可能包括 using-declarations)被发现。在声明集中,using 声明被它们指定的成员替换,并键入声明(包括 injected-class-names) 替换为它们指定的类型。
因此,当您尝试声明struct C
using B1::f; // you hope to make clear that B1::f is to be used
根据查找规则,编译器仍然会找到可能的候选者:因此它仍然是模棱两可的。B1::f
B2::f
评论
C::f