[class.member.lookup] (original) (raw)
11 Classes [class]
Name lookup can result in an ambiguity, in which case the program is ill-formed.
The following steps define the result of name lookup for a member namef in a class scope C.
The lookup set for f in C, called , 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 includingusing-declarations) were found.
In the declaration set,using-declarations are replaced by the set of designated members that are not hidden or overridden by members of the derived class ([namespace.udecl]), and type declarations (including injected-class-names) are replaced by the types they designate.
is calculated as follows:
If C contains a declaration of the name f, the declaration set contains every declaration of f declared inC that satisfies the requirements of the language construct in which the lookup occurs.
[Note 1:
As another example, looking up a name in ausing-declaration ([namespace.udecl]) includes the declaration of a class or enumeration that would ordinarily be hidden by another declaration of that name in the same scope.
— _end note_]
If the resulting declaration set is not empty, the subobject set contains C itself, and calculation is complete.
Otherwise (i.e., C does not contain a declaration of for the resulting declaration set is empty), is initially empty.
If C has base classes, calculate the lookup set for f in each direct base class subobject , and merge each such lookup set in turn into .
The following steps define the result of merging lookup set into the intermediate :
- If each of the subobject members of is a base class subobject of at least one of the subobject members of , or if is empty, is unchanged and the merge is complete.
Conversely, if each of the subobject members of is a base class subobject of at least one of the subobject members of , or if is empty, the new is a copy of . - Otherwise, if the declaration sets of and differ, the merge is ambiguous: the new is a lookup set with an invalid declaration set and the union of the subobject sets.
In subsequent merges, an invalid declaration set is considered different from any other. - Otherwise, the new is a lookup set with the shared set of declarations and the union of the subobject sets.
The result of name lookup for f in C is the declaration set of .
If it is an invalid set, the program is ill-formed.
[Example 1: struct A { int x; }; struct B { float x; }; struct C: public A, public B { }; struct D: public virtual C { }; struct E: public virtual C { char x; }; struct F: public D, public E { }; int main() { F f; f.x = 0; }
is unambiguous because the A and B base class subobjects of D are also base class subobjects of E, so is discarded in the first merge step.
— _end example_]
If the name of an overloaded function is unambiguously found, overload resolution ([over.match]) also takes place before access control.
Ambiguities can often be resolved by qualifying a name with its class name.
[Example 2: struct A { int f();}; struct B { int f();}; struct C : A, B { int f() { return A::f() + B::f(); } }; — _end example_]
[Note 2:
A static member, a nested type or an enumerator defined in a base classT can unambiguously be found even if an object has more than one base class subobject of type T.
Two base class subobjects share the non-static member subobjects of their common virtual base classes.
— _end note_]
[Example 3: struct V { int v;};struct A { int a;static int s;enum { e };};struct B : A, virtual V { };struct C : A, virtual V { };struct D : B, C { };void f(D* pd) { pd->v++; pd->s++; int i = pd->e; pd->a++; } — _end example_]
[Note 3:
When virtual base classes are used, a hidden declaration can be reached along a path through the subobject lattice that does not pass through the hiding declaration.
This is not an ambiguity.
The identical use with non-virtual base classes is an ambiguity; in that case there is no unique instance of the name that hides all the others.
— _end note_]
[Example 4: struct V { int f(); int x; };struct W { int g(); int y; };struct B : virtual V, W { int f(); int x;int g(); int y;};struct C : virtual V, W { };struct D : B, C { void glorp(); };
virt W1 W V V W2 W B B B->W1 B->V C C C->V C->W2 D D D->B D->C
Figure 6: Name lookup [fig:class.lookup]
As illustrated in Figure 6, the names declared in V and the left-hand instance of Ware hidden by those in B, but the names declared in the right-hand instance of W are not hidden at all.
void D::glorp() { x++; f(); y++; g(); } — _end example_]
An explicit or implicit conversion from a pointer to or an expression designating an object of a derived class to a pointer or reference to one of its base classes shall unambiguously refer to a unique object representing the base class.
[Example 5: struct V { };struct A { };struct B : A, virtual V { };struct C : A, virtual V { };struct D : B, C { };void g() { D d; B* pb = &d; A* pa = &d; V* pv = &d; } — _end example_]
[Example 6: struct B1 { void f();static void f(int);int i;};struct B2 { void f(double);};struct I1: B1 { };struct I2: B1 { };struct D: I1, I2, B2 { using B1::f;using B2::f;void g() { f(); f(0); f(0.0); int B1::* mpB1 = &D::i; int D::* mpD = &D::i; } }; — _end example_]