为什么不使用using声明来解决钻石问题?

请考虑以下代码:

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;
}
3个解决方案
56 votes

其他人可以找到标准报价,但我将在概念上进行解释。

这是行不通的,因为using声明仅影响名称查找。

您的using-declaration使名称查找在否则会失败的地方成功,也就是说,它告诉编译器在哪里可以找到函数A。但是它没有告诉它A子对象C作用于哪个对象,即将传递哪个对象。 在调用C时作为隐式A参数。

即使有两个fC子对象,也只有一个函数A,它采用类型B1的隐式A自变量。为了在A对象上调用它,必须将C*始终隐式转换为A*。 ,并且不受任何使用声明的影响。

(如果将数据成员放入A中,这更有意义。那么C每个此类数据成员将有两个。调用f时,如果它访问数据成员,它是否访问从B1继承的27135551400400140140163子对象中的成员,或者 继承自B2A子对象中的那些?)

Brian answered 2020-01-22T07:30:14Z
28 votes

[namespace.udecl] / p17中有一条注释可以直接解决这种情况:

[注意:因为using声明指定了基类成员   (而不是基类的成员子对象或成员函数   子对象),不能使用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. answered 2020-01-22T07:30:48Z
5 votes

除了T.C.的回答外,我还要补充一点,在第10.2节中,标准中非常详细地解释了派生类中的名称查找。

这里是关于使用声明处理的内容:

10.2 / 3:查找集(...)包含两个组件集:声明集,一组名为f的成员; 和子对象集,一组这些对象的声明的子对象(可能包括   使用声明)。 在声明集中,using-declaration被其指定的成员替换,并键入声明(包括   注入的类名)替换为它们指定的类型。

因此,当您尝试在B1::f中声明时

using B1::f; // you hope to make clear that B1::f is to be used

根据查找规则,您的编译器仍然会找到可能的候选对象:B1::fB2::f,因此仍然模棱两可。

Christophe answered 2020-01-22T07:31:26Z
translate from https://stackoverflow.com:/questions/30036255/why-doesnt-a-using-declaration-work-to-solve-the-diamond-problem