Почему не используется декларация использования для решения проблемы алмаза?
Обратите внимание на следующий код:
struct A
{
void f()
{
}
};
struct B1 : A
{
};
struct B2 : A
{
};
struct C : B1, B2
{
void f() // works
{
B1::f();
}
//using B1::f; // does not work
//using B1::A::f; // does not work as well
};
int main()
{
C c;
c.f();
return 0;
}
Я прошу вас не копировать вставить стандартный ответ о том, как решить проблему с алмазом ( "использовать виртуальное наследование" ). Здесь я спрашиваю, почему в этом случае не работает декларация использования. Точная ошибка компилятора:
In function 'int main()':
prog.cpp:31:6: error: 'A' is an ambiguous base of 'C'
c.f();
У меня сложилось впечатление, что использование использования-объявления должно работать из этого примера:
struct A
{
void f()
{
}
};
struct B
{
void f()
{
}
};
struct C : A, B
{
using A::f;
};
int main()
{
C c;
c.f(); // will call A::f
return 0;
}
Ответы
Ответ 1
Кто-то может найти стандартную цитату, но я собираюсь объяснить концептуально.
Это не работает, потому что использование-объявления влияет только на поиск имени.
Ваше объявление-использование заставляет поиск имени успешно работать там, где он иначе терпит неудачу, то есть он сообщает компилятору, где найти функцию f
. Но он не говорит об этом, на который действует объект f
A
, то есть тот, который будет передан как неявный this
параметр при вызове f
.
Существует только одна функция A::f
, хотя существуют два подобъекта A
C
, и он принимает неявный аргумент this
типа A*
. Чтобы вызвать его на объекте C
, C*
должен быть неявно преобразован в A*
. Это всегда неоднозначно, и на него не влияют какие-либо объявления-объявления.
(Это имеет смысл, если вы поместите члены данных внутри A
. Тогда C
будет иметь два из каждого такого элемента данных. Когда вызывается f
, если он обращается к членам данных, он получает доступ к тем из них в субобъект A
, унаследованный от B1
, или те, что находятся в подобъекте A
, унаследованном от B2
?)
Ответ 2
В [namespace.udecl]/p17 есть заметка, которая напрямую обращается к этой ситуации:
[Примечание: поскольку использование-декларация обозначает элемент базового класса (а не субобъект-член или функция-член базового класса подобъект), использование-объявления не может использоваться для разрешения унаследованных член неоднозначности. Например,
struct A { int x(); };
struct B : A { };
struct C : A {
using A::x;
int x(int);
};
struct D : B, C {
using C::x;
int x(double);
};
int f(D* d) {
return d->x(); // ambiguous: B::x or C::x
}
-end note]
Ответ 3
В дополнение к T.C. Ответ. Я хотел бы добавить, что поиск имени в производном классе подробно объясняется в стандарте в разделе 10.2.
Здесь говорится о обработке использования-объявлений:
10.2/3: Набор поиска (...) состоит из двух наборов компонентов: набора деклараций, набора элементов с именем f; и совокупность подобъектов, множество подобъектов, где объявления этих членов (возможно, включая используя-объявления). В объявленном наборе using-declarations заменяются на назначенные им члены и набирают объявления (в том числе имена введенных классов) заменяются типами, которые они обозначают.
Итак, когда вы пытаетесь объявить в struct C
using B1::f; // you hope to make clear that B1::f is to be used
в соответствии с правилами поиска, ваш компилятор все же найдет возможных кандидатов: B1::f
и B2::f
, чтобы он все еще неоднозначный.