C ++ 0x путаница с использованием объявлений - PullRequest
15 голосов
/ 16 апреля 2011

Что должно произойти в этом случае:

struct A {
  void f();
};

struct B : virtual A {
  using A::f;
};

struct C : virtual A {
  using A::f;
};

struct D : B, C { 
  void g() {
    f();
  }
};

Интересующая линия - f().Очевидно, что поиск f в соответствии с 10.2 FDIS завершается успешно и находит A::f.Однако, какие кандидаты будут рассматривать разрешение перегрузки?В спецификации указано: 13.3.1p4:

Для функций без преобразования, введенных объявлением использования в производный класс, функция считается членом производного класса с целью определениятип неявного параметра объекта.

Цель этого состоит в том, чтобы для одного класса, если такой класс содержал как собственные функции-члены, так и объявление использования, переводящее имена функций базового класса в область видимости, чтобы во время разрешения перегрузки все кандидаты-функцииимеют тот же тип класса в своем неявном параметре объекта.Но что это значит для приведенного выше примера?Будут ли следующие кандидаты?

void F1(B&)
void F2(C&)
// call arguments: (lvalue D)

Кажется, это неправильно, потому что у нас есть только одно объявление в наборе результатов поиска в соответствии с 10.2p7.Как мы будем интерпретировать это ??

Ответы [ 4 ]

1 голос
/ 16 апреля 2011

Я думаю, что так как набор поиска, полученный в результате 10.2 / 7, приводит только к одному объявлению, перегрузка функций вообще отсутствует.13.3.1 / 4 будет применяться только в том случае, если / если набор поиска, полученный в результате 10.2 / 7, содержал два или более объявлений.

Редактировать: Возможно, я был не так ясен, как я надеялся.Даже если f перегружен в A, я думаю, что большинство из тех же рассуждений применимы.Возможно, лучше все шаг за шагом.(Обратите внимание, что в этом случае я использую ту же нотацию S (f, X), что и стандарт, но поскольку ваш самый производный класс - D, ваш S (f, D) соответствует их S (f, C)и ваши S (f, B) и S (f, C) соответствуют его S (f, B 1 ) и S (f, B 2 ).

Сначала s (f, D) пусто, потому что у нас нет объявления f, непосредственно содержащегося в D. На основании этого мы получаем 10.2 / 5.

В 10.2 / 6 мы начинаем сслияние s (f, B) с S (f, D). Поскольку s (f, D) в настоящее время пусто, мы следуем второму условию под первой точкой маркера, и S (f, D) становится копией S (f, B).

Затем мы должны объединить S (f, C) с S (f, D). В этом случае каждый из элементов подобъекта S (f, C) является элементом подобъектаS (f, D). Это удовлетворяет первому условию первой точки маркера, поэтому мы оставляем S (f, D) без изменений, и объединение завершено.

В этот момент больше нетбазовые классы B i для рассмотрения, поэтому наши S (f, D) = S (f, B). нет из объявлений from S (f, C) присутствует в конечном наборе перегрузок на всех .

Затем, если S (f, B) содержит две или более функций, мы переходим к 13.3.1.и разрешите набор перегрузки - но поскольку весь набор поступил через B, ситуация, о которой говорится в вопросе, просто не существует.

0 голосов
/ 16 апреля 2011

Я думаю, что ключ находится в 10.2p5, где стандарт относится к проверке каждого «подобъекта прямого базового класса».

Поскольку A фактически наследуется, это «подобъект прямого базового класса»D (10.1p4).

Затем рассматриваются три подобъекта D: A, B и C.По 10.2p6 B и C исключены (A является их основой), и только A::f является кандидатом.

0 голосов
/ 16 апреля 2011

Вместо непосредственного решения вопроса я попытаюсь доказать, что притворство, что существуют функции f в каждом из производных классов, не работает:

Есть только одна функция-кандидат, ион имеет тип

void A::(void)

Хотя вы можете сформировать указатель на член этой функции с помощью

void (A::*F0)(void) = &A::f;
void (B::*F1)(void) = F0;
void (C::*F2)(void) = F0;

Это потому, что указатель на член содержит дополнительную информацию, необходимую для вычисленияпараметр функции.Сайт вызова указателя на член находит подобъект A фактического целевого экземпляра, чтобы предоставить указатель this для f.Внутри функции нет логики для поиска членов A по указателю this производного типа.Так что нельзя говорить о void F1(B* this) и void F2(C* this), как подсказывает ваш вопрос.

Если функции считаются членами производного класса, это как

void B::A::f(void);
void C::A::f(void);

Но какB::A и C::A - это один и тот же базовый класс, в конечном счете в списке кандидатов есть только одна функция, хотя она есть в списке дважды.Затем виртуальное наследование обеспечивает, что оба кандидата вызывают одну и ту же функцию для одного и того же объекта, двусмысленности нет.

0 голосов
/ 16 апреля 2011

Только предположение, полностью не уверен.:)

[ 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) is unambiguous because the A and B base subobjects of D are also base subobjects of E, so S(x,D)
is discarded in the first merge step. —end example ]

Это пример из 10.2p7, где S(f,C) обозначает набор поиска.Предложение, приведенное в конце, является существенным: поскольку D и E имеют одинаковый C базовый класс, а E::x скрывает x от этого C, что делаетокончательное использование F::x однозначно.
Теперь, в вашем примере, ничто не скрывает f базовых классов D, поэтому использование D::f все еще неоднозначно, и я не вижу, как 10.2p7 относится к вашему делу.Как сказано выше, совершенно не уверен.;)

...