Why is the use declaration not used to solve the diamond problem?

Pay attention to the following code:

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; cf(); return 0; } 

I ask you not to copy paste the standard answer on how to solve the diamond problem ("use virtual inheritance"). Here I ask why the declaration of use does not work in this case. Exact compiler error:

 In function 'int main()': prog.cpp:31:6: error: 'A' is an ambiguous base of 'C' cf(); 

I got the impression that using use-declaration should work from this example:

 struct A { void f() { } }; struct B { void f() { } }; struct C : A, B { using A::f; }; int main() { C c; cf(); // will call A::f return 0; } 
+50
c ++ inheritance diamond-problem subclass using-directives
May 04 '15 at 17:31
source share
3 answers

Someone might find a standard quote, but I'm going to explain conceptually.

This does not work, because using-declarations only affects name lookups.

Your use-declaration makes the name lookup work successfully where it otherwise fails, that is, it tells the compiler where to find the function f . But he does not talk about it, on which the object f A acts , that is, the one that will be passed as an implicit this parameter when f called.

There is only one function A::f , although there are two subobjects A C , and it takes an implicit argument of this type of A* . To call it on object C , C* must be implicitly converted to A* . This is always ambiguous, and it is not affected by any advertisements.

(This makes sense if you put the data members inside A Then C will have two of each such data element. When f is called, if it accesses the data members, it gets access to those of them in the subobject A inherited from B1 , or those that are in subobject A inherited from B2 ?)

+56
May 04 '15 at 17:53
source share

There is a note in [namespace.udecl] / p17 that directly addresses this situation:

[Note: since use-declaration refers to an element of a base class (and not a subobject-member or a member function of a base class subobject), use-declarations cannot be used to resolve an inherited member's ambiguity. 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]

+28
May 04 '15 at 17:48
source share

In addition to TC Answer. I would like to add that the name lookup in a derived class is explained in detail in the standard in section 10.2.

It talks about handling use-declarations:

10.2 / 3: The search set (...) consists of two sets of components: a set of declarations, a set of elements with the name f; and a collection of subobjects, many subobjects, where the declarations of these members (possibly including using-announcements). In the declared set, using-declarations are replaced by the members assigned to them and typeset declarations (including the names of the classes introduced) are replaced by the types that they denote.

So when you try to declare in struct C

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

according to the search rules, your compiler will still find possible candidates: B1::f and B2::f , so that it is still ambiguous.

+5
May 04 '15 at 17:54
source share



All Articles