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

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;
}

请不要复制粘贴关于如何解决钻石问题的标准回复("use virtual inheritance")。我在这里要问的是,为什么 using 声明在这种情况下不起作用。确切的编译器错误是:

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 中有一条注释直接解决了这种情况:

[ Note: Because a using-declaration designates a base class member (and not a member subobject or a member function of a base class subobject), a using-declaration cannot be used to resolve inherited member ambiguities. For example,

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
}

end note ]

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

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

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

尽管 C 有两个 A 子对象,但只有一个函数 A::f,并且它采用类型 [=19] 的隐式 this 参数=].为了在 C 对象上调用它,C* 必须隐式转换为 A*。这总是不明确的,并且不受任何 using-declarations.

的影响

(如果将数据成员放在 A 中,则更有意义。然后 C 将有两个这样的数据成员。调用 f 时,如果它访问数据成员,它是访问继承自 B1A 子对象中的成员,还是访问继承自 B2A 子对象中的成员?)

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

这里是关于处理 using-declarations 的说法:

10.2/3: The lookup set (...) consists of two component sets: the declaration set, a set of members named f; and the subobject set, a set of subobjects where declarations of these members (possibly including using-declarations) were found. In the declaration set, using-declarations are replaced by the members they designate, and type declarations (including injected-class-names) are replaced by the types they designate.

因此,当您尝试在 struct C

中声明时
using B1::f; // you hope to make clear that B1::f is to be used

根据查找规则,您的编译器仍然会找到可能的候选者:B1::fB2::f,因此它仍然不明确。