What is the expected behavior?

The following is a purely academically invented class hierarchy.

struct X{ void f1(); void f2(); void f3(); }; struct Y : private X{ void f4(); }; struct Z : X{ }; struct D : Y, Z{ using X::f2; using Z::X::f3; }; int main(){} 

I was expecting the use of the declaration for X :: f2 to be ambiguous, since ā€œXā€ is an ambiguous base for ā€œDā€ (viscosity and availability of X). However g ++ (ideone.com) compiles it in order.

I checked with Online Comeau and it gives an error when using the declaration for X :: f2, as expected. However, this gives ambiguity for using the declaration for Z :: X :: f3.

So what is the expected behavior?

Change 1:

It would be useful to refer to the relevant section of the Standard.

Edit 2:

I checked with VS 2010, and he has an objection only using the X :: f2 declaration. However, this is not about the ambiguity of "X" (as is the case with gcc and Comeau). This is "error C2876:" X ": not all overloads are available."

Edit 3:

 struct X{ void f(){} }; struct Y : X{ struct trouble{ void f(){} }; }; struct trouble : X{ }; struct letscheck : Y, trouble{ using trouble::f; }; int main(){} 

Here I tried to (purposefully) create a type problem in using declarations. Gcc is still compiling this tone and VS2010. Como still gives an error (as expected) about issues of ambiguous types. Based on the explanations for the initial queries, it seems that GCC and VS2010 are wrong. It is right?

+7
c ++ declaration using ambiguity
source share
3 answers

I do not think that any of them are poorly formed. First, for using X::f2 , X scanned, and this will uniquely give the type of class X Then f2 in X scanned, and this is also unambiguous (it is not scanned in D !).

The second case will work for the same reason.

But if you call f2 on object D , the call will be ambiguous, because the name f2 looked up in all subobjects D type X , and D has two such subobjects, and f2 is a non-static member function. The same reason holds for the second case. It doesn't matter if you call f3 directly with Z::X or X Both of them stand for class X

To get ambiguity for a using declaration, you need to write it in different ways. Note that in C ++ 0x using ThisClass::...; not valid. This is in C ++ 03, though, if the whole name refers to a member of the base class.

Conversely, if it is allowed in C ++ 0x, all use of the declaration will also be valid, because C ++ 0x does not take into account subobjects to search for a name: D::f2 uniquely refers to only one declaration (the one in X ). See DR # 39 and final article N1626 .

 struct D : Y, Z{ // ambiguous: f2 is declared in X, and X is a an ambiguous base class using D::f2; // still fine (if not referred to by calls/etc) :) using Z::X::f3; }; struct E : D { // ambiguous in C++03 // fine in C++0x (if not referred to by an object-context (such as a call)). using D::f2; }; 

The C ++ 03 standard describes this in paragraphs 10.2 and 3.4.3.1 .


Answer for Edit3 :

Yes, GCC and VS2010 are wrong. trouble refers to the type found in the name of the introduced class ::trouble , and to the nested class found as Y::trouble . The trouble name preceding :: is looked up using an unqualified search (through 3.4.1/7 , which delegates 10.2 in the first pool), ignoring any names of objects, functions and enumerators ( 3.4.3/1 - there are no such names in this case, although). He then violates requirement 10.2 :

If the result set of ads is not all of the sub-objects of the same type ... the program is poorly formed.


It is possible that VS2010 and GCC interpret the C ++ 0x wording differently from Comeau, and retroactively implement this wording:

In a usage declaration used as a member declaration, the sub-name specifier must name the base class of the class being defined.

This means that not base classes are considered, but this is an error if a non-base class is named. If the Standard intends to ignore the names of a non-base class, it would say that it can only be here, or specify it explicitly (both practices are fulfilled). The standard, however, is in no way associated with its use and can. And GCC implements the C ++ 0x wording because it rejects otherwise completely fine C ++ 03 code, because the usage declaration contains its class name.

As an example of a fuzzy formulation, consider the following expression:

 a.~A(); 

This is syntactically ambiguous because it can be a call to a member function if a is an object of a class, but it can be a pseudo-destructor call (which is a non-operator) if a has a scalar of type (e.g. int ). But what the Standard says for the pseudo-destructor invocation syntax and access to the class member in 5.2.4 and 5.2.5 respectively

The left side of the point operator must be scalar type.

For the first parameter (point), the type of the first expression (object expression) must be a "class object" (full type).

This is a misnomer because it does not eliminate ambiguity at all. It should use "can only", and compilers interpret it that way. This is mainly due to historical reasons, as recently a committee member recently told me to fall asleep. See Rules for the Design and Compilation of International Standards , Appendix H.

+2
source share

using X :: f2; should not work due to private inheritance below code

 struct Y : private X{ void f4(); }; 

It is not possible to access elements from X to Y. Thus, X :: f2 conflicts.

Z::X::f2 should work. Or Z::f2 should work.

0
source share

First, to clarify Johannes answer. When you say using Z::X::f2; , the compiler does not "build the path" to f2 to keep track of how it should be accessible. Since Z::X is the same as X , the declaration is exactly the same as the expression using X::f2; . Compare this with this example:

 struct A { void f() {} void g() {} }; struct B { void f() {} void g() {} }; struct C { typedef AX; }; struct D { typedef BX; }; struct E : A, B { using C::X::f; // C::X == A using D::X::g; // D::X == B }; 

The Z::X syntax does not work because of inheritance or membership, but because the identifier X is accessible from area Z You are even allowed to write Z::Z::Z::Z::X::X::X::X ad nauseam, because each class introduces its own name in its own area. So :: here does not express inheritance.

Now to solve the problem. f2 inherited by Y and Z from X Thus, he is a first-class member of Y and Z E does not need to know about X , because this is a hidden implementation detail. So you want

 struct D : Y, Z{ using Y::f2; // error: inaccessible using Z::f3; }; 

Explain in terms of 9.1 / 2 how you ask:

The class name is inserted into the area in which it is declared immediately after the class name is seen. The class name is also inserted into the scope of the class itself; this is known as the injected class name.

The name X is entered in X as X::X Then it is inherited in Y and Z Y and Z implicitly declare X in their area.

10.2 / 2:

The following steps determine the result of searching for a name in the class Scope, C. First, each expression for the name in the class and in each of its sub-objects of the base class is considered .... If the result set of declarations is not all of the sub-objects are the same a type or set has a non-static member and includes members from separate sub-objects; there is ambiguity and is poorly formed. Otherwise, this set is a search result.

Note that I have highlighted the wordy sub-objects of the word. Although the name X is in two sub-objects, they are the same, namely X

0
source share

All Articles