13 Derived classes [class.derived]

13.2 Member name lookup [class.member.lookup]

Поиск имени члена определяет значение имени ( id-expression) в файлеclass scope. Поиск имени может привести к ошибкеambiguity, и в этом случае программа имеет неправильный формат . Для того id-expression, поиск имя начинается в классе сфереthis; для qualified-idпоиска по имени начинается в области nested-name-specifier. Name lookup имеет место раньше access control.

Следующие шаги определяют результат поиска имени для имени члена f в области классаC.

lookup set Дляf инC, называетсяS(f,C), состоит из двух наборов компонентов: вdeclaration set, набор членов с именемf; иsubobject setнабор подобъектов, в которых using-declarationsбыли найдены объявления этих членов (возможно, включая ). В наборе объявлений using-declarations они заменяются набором назначенных членов, которые не скрыты и не переопределяются членами производного класса ([namespace.udecl]), а объявления типов (включая имена внедренных классов) заменяются типами, которые они обозначают.S(f,C) рассчитывается следующим образом:

ЕслиC содержит объявление имениf, набор объявлений содержит каждое объявление,f объявленное в, C которое удовлетворяет требованиям языковой конструкции, в которой выполняется поиск. [ Note: Поиск имени в elaborated-type-specifier([basic.lookup.elab]) или base-specifier, например, игнорирует все объявления, не относящиеся к типу, а поиск имени в nested-name-specifier([basic.lookup.qual]) игнорирует объявления функций, переменных и перечислителей. В качестве другого примера, поиск имени в a using-declarationвключает в себя объявление класса или перечисления, которое обычно было бы скрыто другим объявлением этого имени в той же области. ] Если результирующий набор объявлений не пустой, набор подобъектов содержит сам себя, и расчет завершен.end noteC

В противном случае (т.е.C не содержит объявленияf или результирующий набор объявлений пуст)S(f,C) изначально пуст. ЕслиC есть базовые классы, вычислите поисковый набор дляf каждого прямого подобъекта базового классаBiи объедините каждый такой поисковый набор S(f,Bi) по очереди вS(f,C).

Следующие шаги определяют результат слияния набора поискаS(f,Bi) с промежуточнымS(f,C):

  • Если каждый из членов подобъектаS(f,Bi) является подобъектом базового класса по крайней мере одного из членов подобъектаS(f,C)или S(f,Bi) пуст,S(f,C) не изменяется и слияние завершается. И наоборот, если каждый из членов подобъектаS(f,C) является подобъектом базового класса по крайней мере одного из членов подобъектаS(f,Bi), или, если S(f,C) он пуст, новыйS(f,C) является копиейS(f,Bi).

  • В противном случае, если наборы объявленийS(f,Bi) иS(f,C) различаются, слияние будет неоднозначным: новыйS(f,C) набор поиска с недопустимым набором объявлений и объединением наборов подобъектов. При последующих слияниях недействительный набор объявлений считается отличным от любого другого.

  • В противном случае новыйS(f,C) - это поисковый набор с общим набором объявлений и объединением наборов подобъектов.

Результатом поиска имениf вC является набор объявленийS(f,C). Если это недопустимый набор, программа имеет неправильный формат. [Example:

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: public virtual C { };         // S(x,D) = S(x,C)
struct E: public virtual 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) однозначно , так какA иB базовые субобъекты класса изD также базовые субобъекты класса изE, так чтоS(x,D) отбрасываются на первом этапе слияния. ]end example

Если имя перегруженной функции обнаружено однозначно, overload resolution также выполняется до управления доступом. Неоднозначности часто можно разрешить, уточнив имя с именем класса. [Example:

struct A {
  int f();
};
struct B {
  int f();
};
struct C : A, B {
  int f() { return A::f() + B::f(); }
};

end example]

[ Note: Статический член, вложенный тип или перечислитель, определенные в базовом классе, T могут быть однозначно найдены, даже если объект имеет более одного подобъекта базового класса типаT. Два подобъекта базового класса совместно используют нестатические подобъекты-члены своих общих виртуальных базовых классов. ] [end noteExample:

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++;          // 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]

[ Когда используются виртуальные базовые классы, скрытое объявление может быть достигнуто по пути через решетку подобъектов, который не проходит через скрытое объявление. Это не двусмысленность. Идентичное использование с невиртуальными базовыми классами является неоднозначным; в этом случае не существует уникального экземпляра имени, скрывающего все остальные. ] [Note: end noteExample:

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 W1 W V V V W2 W2 W B B B B->W1 B->W1 B->V B->V C C C C->V C->V C->W2 C->W2 D D D D->B D->B D->C D->C
Рисунок6 - Поиск имени

Имена, объявленные вV и левый экземплярW скрыты внутриB, но имена, объявленные в правом экземпляреW , не скрываются вообще.

void D::glorp() {
  x++;              // OK: B​::​x hides V​::​x
  f();              // OK: B​::​f() hides V​::​f()
  y++;              // error: B​::​y and C's W​::​y
  g();              // error: B​::​g() and C's W​::​g()
}

end example]

Явное или неявное преобразование указателя или выражения, обозначающего объект производного класса, в указатель или ссылку на один из его базовых классов должно однозначно относиться к уникальному объекту, представляющему базовый класс. [Example:

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]

[ Note: Даже если результат поиска имени однозначен, использование имени , найденное в нескольких подобъектах все еще может быть неоднозначным ([conv.mem],[expr.ref],[class.access.base]). ] [end noteExample:

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();                        // Ambiguous conversion of this
    f(0);                       // Unambiguous (static)
    f(0.0);                     // Unambiguous (only one B2)
    int B1::* mpB1 = &D::i;     // Unambiguous
    int D::* mpD = &D::i;       // Ambiguous conversion
  }
};

end example]