I don't think that any of these are ill-formed. First, for using X::f2
, X
is looked up, and this will unambiguously yield the class type X
. Then f2
in X
is looked up, and this is unambiguous too (it is not looked up in D
!).
The second case will work for the same reason.
But if you call f2
on a D
object, the call will be be ambiguous because the name f2
is looked up in all subobjects of D
of 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 does not make a difference for this whether you name f3
using Z::X
or X
directly. Both of these designate the class X
.
To get an ambiguity for the using declaration, you need to write it differently. Note that in C++0x using ThisClass::...;
is not valid. It is in C++03 though, as long as the whole name refers to a base-class member.
Conversely, if this would be allowed in C++0x, the whole using declaration would also be valid, because C++0x does not take subobjects into account for name-lookup: D::f2
unambiguously refers to only one declaration (the one in X
). See DR #39 and the final paper 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
.
Response for Edit3:
Yes, GCC and VS2010 are wrong. trouble
refers to the type found by the injected class name of ::trouble
and to the nested class found as Y::trouble
. The name trouble
preceeding the ::
is looked up using unqualified lookup (by 3.4.1/7
, which delegates to 10.2
in the first bullet) ignoring any object, function and enumerator names (3.4.3/1
- there are no such names in this case, though). It then violates against 10.2
's requirement that:
If the resulting set of declarations are not all from sub-objects of the same type ... the program is ill-formed.
It is possible that VS2010 and GCC interpret C++0x wording differently than Comeau and retroactively implement that wording:
In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined.
This means that non-base classes are considered, but it is an error if a non-base class is named. If the Standard would intend to ignore non-base class names, it would say can only here, or spell it out explicitly (both practices are done). The Standard however is not at all consequent with its use of shall and can. And GCC implements C++0x wording, because it rejects otherwise completely fine C++03 code, just because the using declaration contains its class-name.
For an example of the unclear wording, consider the following expression:
a.~A();
This is syntactically ambiguous, because it can be a member function call if a
is a class object, but it can be a pseudo-destructor-call (which is a no-op) if a
has a scalar type (such as int
). But what the Standard says is for the syntax of a pseudo-destructor call and class member access at 5.2.4
and 5.2.5
respectively
The left-hand side of the dot operator shall be of scalar type.
For the first option (dot) the type of the first expression (the object expression ) shall be “class object” (of a complete type).
That is the wrong use, because it does not clear up the ambiguity at all. It should use "can only", and compilers interpret it in that way. This has mostly historical reasons, as some committee-member recently told me on usenet. See The rules for the structure and drafting of International Standards, Annex H.