Подтвердить что ты не робот

Почему не используется декларация использования для решения проблемы алмаза?

Обратите внимание на следующий код:

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;
}
4b9b3361

Ответ 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, чтобы он все еще неоднозначный.