С++ 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
является кандидатом.