Below 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 expected using declaration for X::f2 to be ambiguous as 'X' is an ambiguous base of 'D' (visbility vs accessibility of X). However g++ (ideone.com) compiles it fine.
I checked with Online Comeau and it gives error in using declaration for X::f2 as expected. However it gives ambiguity for using declaration for Z::X::f3 as well.
So what is the expected behavior?
Edit 1:
A reference to the appropriate section of the Standard would be helpful, please.
Edit 2:
I checked with VS 2010 and it has objections only with the using declaration X::f2. However it is not about ambiguity of 'X' (as in the case of gcc and Comeau). It is about "error C2876: 'X' : not all overloads are accessible".
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 have attempted (purposefully) to create an issue with types in using declaration. Gcc still compiles this fine and so does VS2010. Comeau still gives error (as expected) about ambiguous types 'trouble'. Going by explanations given for the initial queries, it appears GCC and VS2010 are wrong. Is that correct?
Answer and Explanation: The answer is c) role. Role specify a set of expected behavior patterns attributed to someone occupying a given position in a social unit.
The expected behavior of the program is that the program will output Goodbye! to the screen/console. print() function will print the output to the console/screen.
Unexpected behaviours are actions which do not follow the social rules. They surprise other people and may make them have uncomfortable thoughts about us. It's important to note that these are not necessarily bad thoughts, nor thinking WE are weird, but uncomfortable feelings or ideas.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With