С++ 0x путаница с использованием объявлений

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

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. Как мы это интерпретируем?

Ответы

Ответ 1

Я думаю, что, поскольку набор поиска, полученный в результате 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) ans 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). Ни одно из объявлений из S (f, C) не присутствует в окончательной полной загрузке.

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

Ответ 2

Только спекуляция, совершенно не уверена.:)Суб >

[ 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 unambigiuous.
Теперь в вашем примере ничего не скрывает f базовых классов D, поэтому использование D::f все еще неоднозначно, и я не вижу, как 10.2p7 относится к вашему делу. Как сказал наверху, совершенно не уверен.;)

Ответ 3

Вместо того, чтобы напрямую обращаться к вопросу, я попытаюсь утверждать, что притвориться, что существуют 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 являются одним и тем же базовым классом, в конечном итоге в списке кандидатов есть только одна функция, хотя она находится в списке дважды. Тогда виртуальное наследование обеспечивает, чтобы оба кандидата вызывали одну и ту же функцию на одном и том же объекте, нет двусмысленности.

Ответ 4

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

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

Тогда рассматриваются три подобъекта D: A, B и C. На 10.2p6 B и C устраняются (A является их базой), и только A::f является кандидатом.