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

时间:2015-05-04 17:31:27

标签: c++ inheritance subclass using-directives 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();

我得到的印象是,使用声明应该可以从这个例子开始:

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 个答案:

答案 0 :(得分:56)

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

它不起作用,因为 using-declaration 仅影响名称查找。

using-declaration 会导致名称查找成功,否则会失败,也就是说,它会告诉编译器在哪里找到函数f但它并没有告诉它 A子对象f作用于,也就是说,当this时,哪一个将作为隐式f参数传递被称为。

即使A::f有两个A子对象,也只有一个函数C,它采用类型为this的隐式A*参数。要在C对象上调用它,C*必须隐式转换为A*。这总是不明确的,不受任何 using-declarations 的影响。

(如果您将数据成员放在A中,这会更有意义。然后C会有两个这样的数据成员。当f被调用时,如果它访问数据成员,它是否访问从A继承的B1子对象或从A继承的B2子对象中的对象?)

答案 1 :(得分:28)

[namespace.udecl] / p17中的注释直接解决了这种情况:

  

[注意:因为 using-declaration 指定了基类成员   (而不是成员子对象或基类的成员函数   子对象), 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
}
     

- 结束记录]

答案 2 :(得分:5)

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

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

  

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

因此,当您尝试在tv.setMaxWidth(((LinearLayout)tv.getParent()).getWidth()/3);

中声明时
struct C

根据查找规则,您的编译器仍会找到可能的候选者:using B1::f; // you hope to make clear that B1::f is to be used B1::f,因此它仍然不明确。