каково ожидаемое поведение?

Ниже приведена чисто академически придуманная иерархия классов.

struct X{
        void f1();
        void f2();
        void f3();
};

struct Y : private X{
        void f4();
};

struct Z : X{
};

struct D : Y, Z{
        using X::f2;
        using Z::X::f3;
};

int main(){}

Я ожидал, что использование объявления для X::f2 будет неоднозначным, поскольку «X» является неоднозначной основой «D» (видимость и доступность X). Однако g++ (ideone.com) компилирует его нормально.

Я проверил с помощью Online Comeau, и он выдает ошибку при использовании объявления для X::f2, как и ожидалось. Однако это также дает неоднозначность при использовании объявления для Z::X::f3.

Итак, каково ожидаемое поведение?

Редактировать 1:

Пожалуйста, укажите ссылку на соответствующий раздел Стандарта.

Редактировать 2:

Я проверил с VS 2010, и у него есть возражения только с использованием объявления X::f2. Однако дело не в неоднозначности «X» (как в случае с gcc и Comeau). Речь идет об "ошибке C2876: 'X': доступны не все перегрузки".

Редактировать 3:

struct X{
    void f(){}
};

struct Y : X{
    struct trouble{
        void f(){}
    };

};

struct trouble : X{
};

struct letscheck : Y, trouble{
    using trouble::f;
};

int main(){}

Здесь я попытался (целенаправленно) создать проблему с типами при использовании объявления. Gcc по-прежнему прекрасно компилирует это, как и VS2010. Комо по-прежнему выдает ошибку (как и ожидалось) о неоднозначных типах «проблем». Судя по объяснениям, данным для первоначальных запросов, GCC и VS2010 ошибаются. Это правильно?


person Chubsdad    schedule 23.08.2010    source источник
comment
В D добавьте метод, который вызывает f2(), посмотрите, что произойдет.   -  person Martin York    schedule 23.08.2010


Ответы (3)


Я не думаю, что кто-то из них плохо сформирован. Во-первых, для using X::f2 ищется X, и это однозначно даст тип класса X. Затем ищется f2 в X, и это тоже однозначно (в D не ищется!).

Второй случай будет работать по той же причине.

Но если вы вызовете f2 для объекта D, вызов будет неоднозначным, поскольку имя f2 ищется во всех подобъектах D типа X, а D имеет два таких подобъекта, а f2 является нестатическая функция-член. Та же причина справедлива и для второго случая. Для этого не имеет значения, называете ли вы f3 напрямую через Z::X или X. Оба они обозначают класс X.

Чтобы получить неоднозначность объявления использования, вам нужно написать его по-другому. Обратите внимание, что в C++0x using ThisClass::...; недопустимо. Однако это в С++ 03, если все имя относится к члену базового класса.

И наоборот, если бы это было разрешено в C++0x, все объявление using также было бы действительным, поскольку C++0x не учитывает подобъекты при поиске по имени: D::f2 однозначно относится только к одному объявлению (тот, что в X). См. DR 39 и заключительный документ. N1626.

struct D : Y, Z{
    // ambiguous: f2 is declared in X, and X is a an ambiguous base class
    using D::f2;
 
    // still fine (if not referred to by calls/etc) :)
    using Z::X::f3;
};

struct E : D {
  // ambiguous in C++03
  // fine in C++0x (if not referred to by an object-context (such as a call)).
  using D::f2;
};

Стандарт C++03 описывает это в параграфах 10.2 и 3.4.3.1.


Ответ для Edit3:

Да, GCC и VS2010 ошибаются. trouble относится к типу, найденному по введенному имени класса ::trouble, и к вложенному классу, найденному как Y::trouble. Имя trouble, предшествующее ::, ищется с использованием неквалифицированного поиска (с помощью 3.4.1/7, который делегирует 10.2 в первом маркере), игнорируя любые имена объектов, функций и перечислителей (3.4.3/1 — в данном случае таких имен нет). Затем это нарушает требование 10.2 о том, что:

Если результирующий набор объявлений не все из подобъектов одного и того же типа... программа неправильно сформирована.


Возможно, VS2010 и GCC интерпретируют формулировку C++0x иначе, чем Comeau, и задним числом реализуют эту формулировку:

В объявлении-использования, используемом в качестве объявления-члена, спецификатор вложенного-имени должен называть базовый класс определяемого класса.

Это означает, что небазовые классы учитываются, но если указан небазовый класс, это будет ошибкой. Если бы Стандарт намеревался игнорировать имена небазовых классов, он сказал бы здесь can only или указал бы это явно (оба метода выполняются). Однако Стандарт вовсе не является следствием использования в нем слов должен и может. И GCC реализует формулировку C++0x, потому что он отвергает совершенно прекрасный код C++03, просто потому, что объявление using содержит его имя класса.

В качестве примера неясной формулировки рассмотрим следующее выражение:

a.~A();

Это синтаксически неоднозначно, потому что это может быть вызов функции-члена, если a является объектом класса, но это может быть вызов псевдодеструктора (который не является операцией), если a имеет скалярный тип (например, int). Но то, что говорит Стандарт, касается синтаксиса вызова псевдодеструктора и доступа к члену класса в 5.2.4 и 5.2.5 соответственно.

Левая часть оператора точки должна быть скалярного типа.

Для первого варианта (точка) тип первого выражения (выражения объекта) должен быть «объект класса» (полного типа).

Это неправильное использование, потому что оно вообще не устраняет двусмысленность. Он должен использовать только can, и компиляторы интерпретируют его таким образом. У этого в основном исторические причины, как недавно сказал мне один член комитета в юзнете. См. Правила структуры и составления международных стандартов, Приложение H.

person Johannes Schaub - litb    schedule 23.08.2010
comment
@litb: я изменил класс 'D' следующим образом: struct D : Y, Z{ using X::f2; используя Z::X::f3; void f(){X *p = this;} }; Теперь я получил ошибку prog.cpp: 19: ошибка: повторение использования объявления «using X:: f3» prog.cpp: в функции-члене «void D:: f()»: prog.cpp: 20: ошибка: «X ' является неоднозначной основой 'D' Отличаются ли правила для 'использования объявления' во время 'поиска имени' - person Chubsdad; 23.08.2010
comment
@chubsdad все, что я могу сделать, это повторить Стандарт. Я не знаю, что реализуют эти компиляторы. Я не вижу повторного использования объявления в вашем коде. - person Johannes Schaub - litb; 23.08.2010
comment
@litb: небольшая опечатка в моем коде и, следовательно, в выводе компилятора. Ниже приведена исправленная ошибка: - person Chubsdad; 23.08.2010
comment
@chubsdad, я рекомендую вам прочитать 10.2 на С++ 03, чтобы понять, что происходит. Преобразование не происходит, если вы пишете объявление using (результат поиска просто будет неоднозначным). И в C++03 не происходит преобразования, если вы ищете f2 позже в D вызов, который был бы неправильно сформирован. В C++0x происходит преобразование, необходимое для пересмотренной формулировки 10.2, описанной в 5.2.5/5 в FCD, чтобы диагностировать правило множественных подобъектов. В C++0x это не неоднозначный поиск, а неоднозначное преобразование, что является ошибкой. - person Johannes Schaub - litb; 23.08.2010
comment
@chubsdad я добавил кое-что забавное. Надеюсь, ты не против. Это также должно немного прояснить ситуацию, я надеюсь :) - person Johannes Schaub - litb; 23.08.2010
comment
@litb: Пожалуйста, учитесь у всех гуру здесь. А обучение с «развлечением» — это высшее блаженство. Кстати, почему вы удалили этот комментарий, относящийся к «типу и нескольким подобъектам». Это был комментарий "в яблочко" - person Chubsdad; 23.08.2010
comment
@chubsdad я подумал, что комментарий был неточным, и мне пришлось бы отправить его повторно (заменив объект нестатической функцией или членом данных), но потом решил, что лучше позволить @Potatoswatter ответить :) - person Johannes Schaub - litb; 23.08.2010
comment
@litb,@potatoswatter: Я также попытался немного развлечься и создал Edit 3. Пожалуйста, проверьте и подтвердите мое понимание всех комментариев от вас. - person Chubsdad; 23.08.2010
comment
@chubsdad, пожалуйста, обратите внимание, что pottoswatter не получит это сообщение, потому что он не появился в этой ветке комментариев. Уведомления будут получать только первый никнейм, появившийся после '@', и только ники, участвующие в этой ветке. Но мне такое поведение не нравится :( - person Johannes Schaub - litb; 23.08.2010
comment
@litb: я изо всех сил стараюсь идти с тобой в ногу. Я заблудился на Это означает, что учитываются небазовые классы, но это ошибка, если назван небазовый класс.. Как можно прийти к такому выводу? - person Chubsdad; 23.08.2010
comment
@chubsdad см. обсуждение аналогичной проблемы здесь: groups.google .com/group/comp.std.c++/browse_thread/thread/ . Редактор Standard Пит Беккер в конце хорошо проясняет ситуацию. - person Johannes Schaub - litb; 23.08.2010

используя X::f2; не должно работать из-за частного наследования кода ниже

struct Y : private X{
    void f4();
};

Невозможно получить доступ к членам X через Y. Таким образом, X::f2 будет конфликтовать.

Z::X::f2 должно работать. Или Z::f2 должно работать.

person bjskishore123    schedule 23.08.2010
comment
C++ имеет правило множественного доступа. Если имя можно найти по нескольким путям в иерархии базового класса, и один из них является общедоступным, используется этот общедоступный путь. См. 11.7/1. - person Johannes Schaub - litb; 23.08.2010
comment
@litb: Таким образом, это означает, что «использование X :: f2» нормально, поскольку «X» доступен через «Z» в OP. Но тогда «X» является неоднозначным и, следовательно, ошибкой, связанной с двусмысленностью. Итак, сообщение об ошибке VS2010 о том, что «не все перегрузки доступны», вероятно, сложно. Это означает, что Комо прав. Меньше всего я подозревал ошибку использования объявления для 'Z::X::f3'. - person Chubsdad; 23.08.2010
comment
@chubsdad это просто означает, что это не ошибка доступности. Он изменяет доступ к пути, который дает наибольший доступ. Это не было бы двусмысленным поиском даже без 11.7/1, но тогда мы бы не знали, нужно ли нам применять частную или общедоступную доступность. - person Johannes Schaub - litb; 23.08.2010

Во-первых, чтобы уточнить ответ Йоханнеса. Когда вы говорите using Z::X::f2;, компилятор не «строит путь» к f2, чтобы отслеживать, как к нему следует обращаться. Поскольку Z::X — это то же самое, что и X, объявление точно такое же, как и using X::f2;. Сравните это с этим примером:

struct A { void f() {} void g() {} };
struct B { void f() {} void g() {} };
struct C { typedef A X; };
struct D { typedef B X; };
struct E : A, B {
    using C::X::f; // C::X == A
    using D::X::g; // D::X == B
};

Синтаксис Z::X работает не из-за наследования или членства, а потому, что идентификатор X доступен из области Z. Вам даже разрешено писать Z::Z::Z::Z::X::X::X::X до отвращения, потому что каждый класс вводит свое имя в свою область видимости. Таким образом, :: здесь не выражает наследование.

Теперь, чтобы решить проблему. f2 наследуется Y и Z от X. Таким образом, это первоклассный член Y и Z. E не нужно знать о X, потому что это скрытая деталь реализации. Итак, вы хотите

struct D : Y, Z{
    using Y::f2; // error: inaccessible
    using Z::f3;
};

Чтобы объяснить с точки зрения 9.1/2, как вы спрашиваете:

Имя класса вставляется в область видимости, в которой оно объявлено, сразу после того, как имя класса будет замечено. Имя класса также вставляется в область видимости самого класса; это известно как введенное имя класса.

Имя X вставляется в X как X::X. Затем он наследуется в Y и Z. Y и Z не объявляют X неявно в своей области.

10.2/2:

Следующие шаги определяют результат поиска имени в области класса C. Сначала рассматривается каждое объявление имени в классе и в каждом из его подобъектов базового класса. … Если результирующий набор объявлений не все из подобъектов одного типа или набор имеет нестатический член и включает элементы из разных подобъектов, возникает неоднозначность, и программа плохо сформированный. В противном случае этот набор является результатом поиска.

Обратите внимание, что я выделил подобъекты во множественном числе жирным шрифтом. Хотя имя X встречается в двух подобъектах, оба они относятся к одному типу, а именно X.

person Potatoswatter    schedule 23.08.2010
comment
В области «D» есть два подобъекта «X» (один из «Y» (недоступный) и один из «Z» (доступный)). Со ссылкой на $9.2 (внедренное имя класса). Разве «использование X::f2» не должно быть двусмысленным с приведенной выше логикой, поскольку «X» является неоднозначной основой «D»? - person Chubsdad; 23.08.2010
comment
@chubs: существует только один класс с именем X. X называет класс, а не подобъект: в этом суть моей иллюстрации. - person Potatoswatter; 23.08.2010
comment
Спасибо за разъяснения. Отредактировали вопрос (Редактировать 3) и хотели, чтобы вы ответили, насколько я понимаю. - person Chubsdad; 23.08.2010