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

Пожалуйста, рассмотрите следующий код:

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

person gd1    schedule 04.05.2015    source источник
comment
Вам нужен виртуальный метод :: f as, чтобы переопределить его.   -  person progsource    schedule 04.05.2015
comment
Я ничего не переопределяю. я прячусь.   -  person gd1    schedule 04.05.2015
comment
Работает здесь. Какой компилятор вы используете?   -  person 0x499602D2    schedule 04.05.2015
comment
Вероятно: нет B1::f (имя разрешается как A::f), поэтому у вас есть два A::f (один в B1 и один в B2)   -  person    schedule 04.05.2015
comment
@ 0x499602D2: удалите определение C::f.   -  person Jarod42    schedule 04.05.2015
comment
Вероятно, это связано с тем, как выполняется поиск имени. Кажется, что директива using добавлена ​​в набор перегрузки после поиска неполного имени. В частности, если вы раскомментируете директиву using, но удалите явный вызов c.f(), ваша программа скомпилируется.   -  person vsoftco    schedule 04.05.2015
comment
Что касается заголовка, вы стали несчастной жертвой правила, которое в огромном большинстве случаев является чистой выгодой.   -  person Lightness Races in Orbit    schedule 05.05.2015
comment
@LightnessRacesinOrbit: я добавил +1 к вашему комментарию, потому что вы проявили сочувствие к моим страданиям, в любом случае я позволю себе не согласиться: такое правило откровенно бессмысленно, потому что оно бесполезно, когда оно не вредно. Плохие парни могут просто написать issue в поле заголовка или proble.m, а затем написать, пожалуйста, помогите, как объявить указатель cheerz в теле. Вас не спасает автоматический фильтр слов, вам придется выискивать некачественные вопросы один за другим. И позвольте мне написать проблему с бриллиантами, ради бога. :)   -  person gd1    schedule 05.05.2015
comment
@Kobi: Суть в том, что это [s]ane. :)   -  person gd1    schedule 05.05.2015
comment
Более актуальным является meta.stackexchange.com/questions/108815/   -  person Lightness Races in Orbit    schedule 05.05.2015


Ответы (3)


Кто-то еще может найти стандартную цитату, но я объясню концептуально.

Это не работает, потому что объявление-использования влияет только на поиск имени.

Ваше объявление-использования приводит к успешному поиску имени там, где в противном случае это было бы ошибкой, т. е. оно сообщает компилятору где найти функцию f, но не сообщает ему < em>какой A подобъект f действует, то есть какой из них будет передан как неявный параметр this при вызове f.

Существует только одна функция A::f, хотя есть два A подобъекта C, и она принимает неявный аргумент this типа A*. Чтобы вызвать его для объекта C, C* необходимо неявно преобразовать в A*. Это всегда неоднозначно, и на него не влияют никакие декларации использования.

(Это имеет больше смысла, если вы поместите элементы данных внутри A. Тогда C будет иметь по два таких члена данных. Когда f вызывается, если он обращается к элементам данных, обращается ли он к тем, которые находятся в подобъекте A, унаследованном от B1, или те, что в подобъекте A унаследованы от B2?)

person Brian Bi    schedule 04.05.2015
comment
Таким образом, использование-декларирование недостаточно умно, иначе он должен получить доступ к тем, которые в A унаследованы от B1. - person user1633272; 31.03.2017

В [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
}

конец примечания ]

person T.C.    schedule 04.05.2015
comment
Большое спасибо. Я готов принять этот ответ, но не могли бы вы объяснить мне своими словами, что они означают для обозначения члена базового класса (а не подобъекта-члена или функции-члена подобъекта базового класса). Эм... что? - person gd1; 04.05.2015
comment
@Т.С. Нормативная ссылка, по-видимому, [expr.ref]/5. - person Brian Bi; 04.05.2015
comment
@gd1 Объявление использования обозначает член (A::x()), а не подобъект (либо C, либо B, содержащий A, содержащий x()). Вам нужно указать подобъект, чтобы устранить двусмысленность. - person Barry; 04.05.2015
comment
@ Брайан Конечно, это то, что в конечном итоге делает вызов неправильным, но для завершения вам нужно пройти гораздо больше, чем это. Поскольку вопрос не требовал полного языкового анализа, я решил, что заметка достаточно хороша. - person T.C.; 04.05.2015
comment
@ gd1 Я собирался, но затем Брайан опубликовал свой ответ, и мне нечего добавить к нему. - person T.C.; 04.05.2015

В дополнение к ответу TC, я хотел бы добавить, что поиск имени в производном классе довольно подробно объясняется в стандарте в разделе 10.2.

Вот что сказано об обработке using-деклараций:

10.2/3: Набор поиска (...) состоит из двух наборов компонентов: набора объявлений, набора элементов с именем f; и набор подобъектов, набор подобъектов, в которых были найдены объявления этих членов (возможно, включая объявления использования). В наборе объявлений декларации использования заменяются членами, которые они обозначают, а объявления типов (включая имена внедренных классов) заменяются типами, которые они обозначают.

Итак, когда вы пытаетесь объявить в struct C

using B1::f; // you hope to make clear that B1::f is to be used

согласно правилам поиска, ваш компилятор все же находит возможных кандидатов: B1::f и B2::f, так что все равно неоднозначно.

person Christophe    schedule 04.05.2015
comment
Как это связано с ответом Брайана? - person gd1; 04.05.2015
comment
Очень четкий ответ Брайана был опубликован в то же время. Я бы не опубликовал свой ответ, если бы сначала прочитал его ;-) Но я не удаляю свой, просто чтобы оставить стандартную ссылку для языковых юристов, которые могут быть заинтересованы. - person Christophe; 04.05.2015
comment
Я не думаю, что ваш ответ бесполезен, я просто пытаюсь понять, как он связан с ответом Брайана. Я не понимаю, как 10,2/3 может объяснить то, что я вижу, тогда как примечание Т.С. и объяснение Брайана имеет смысл для меня - при совместном использовании;) - person gd1; 04.05.2015
comment
Я закончил цитату с первого предложения. Мое объяснение сосредоточено исключительно на наборе объявлений (и на том факте, что остается двусмысленность). Брайан обращается также к набору подобъектов и частному случаю вашего примера. - person Christophe; 04.05.2015