The lookup set for f in C, called S(f,C),
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 including
using-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.
If C contains a declaration of the name f, the
declaration set contains every declaration of f declared in
C that satisfies the requirements of the language construct in
which the lookup occurs.
As another example,
looking up a name in a
using-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 f
or the resulting declaration set is empty), S(f,C) is initially empty.
If C has base classes, calculate the lookup set for f in
each direct base class subobject Bi, and merge each such lookup set
S(f,Bi) in turn into S(f,C).
If each of the subobject members of S(f,Bi) is a base class
subobject of at least one of the subobject members of S(f,C), or if
S(f,Bi) is empty, S(f,C) is unchanged and the merge is complete.
Conversely, if each of the subobject members of S(f,C) is a base class
subobject of at least one of the subobject members of S(f,Bi), or if
S(f,C) is empty, the new S(f,C) is a copy of S(f,Bi).
Otherwise, if the declaration sets of S(f,Bi) and S(f,C)
differ, the merge is ambiguous: the new S(f,C) 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.
The result of name lookup for f in C is the declaration
set of S(f,C).
If it is an invalid set, the program is ill-formed.
[Example 1: struct A {int x; }; // S(x,A) = { { A::x }, { A } }struct B {float x; }; // S(x,B) = { { B::x }, { B } }struct C:public A, public B {}; // S(x,C) = { invalid, { A in C, B in C } }struct D:publicvirtual C {}; // S(x,D) = S(x,C)struct E:publicvirtual C {char x; }; // S(x,E) = { { E::x }, { E } }struct F:public D, public E {}; // S(x,F) = S(x,E)int main(){
F f;
f.x =0; // OK, lookup finds E::x}
S(x,F) is unambiguous because the A and B base
class subobjects of D are also base class subobjects of E, so
S(x,D) is discarded in the first merge step.
A static member, a nested type or an enumerator defined in a base class
T 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;
staticint s;
enum{ e };
};
struct B : A, virtual V {};
struct C : A, virtual V {};
struct D : B, C {};
void f(D* pd){
pd->v++; // OK: only one v (virtual)
pd->s++; // OK: only one s (static)int i = pd->e; // OK: only one e (enumerator)
pd->a++; // error: ambiguous: two as in D} — end example]
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.
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(); };
As illustrated in Figure 6,
the names declared in V and the left-hand instance of W
are hidden by those in B, but the names declared in the
right-hand instance of W are not hidden at all.
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; // error: ambiguous: C's A or B's A?
V* pv =&d; // OK: only one V subobject} — end example]
Even if the result of name lookup is unambiguous, use of a name found in
multiple subobjects might still be
ambiguous ([conv.mem], [expr.ref], [class.access.base]).