Объявление класса в той же области, что и при использовании компиляции объявлений в GCC, но не в MSVS

Корректна ли следующая программа в соответствии со стандартом С++?

namespace X { class A; }

namespace Y { using X::A; class A {}; }

int main() {}

Я получаю разные результаты с разными компиляторами:

  • gcc компилирует без ошибок.
  • Visual С++ выдает ошибку C2888: «X :: A»: символ не может быть определен в пространстве имен «Y»

Я не нахожу в стандарте С++ ни одного правила, которое нарушает моя программа.

Если программа правильно построена, то почему Visual Studio выдает ошибку?

Если программа неправильно сформирована, какое правило стандарта С++ она нарушила и почему gcc не выдает ошибку?

Я не пытаюсь компилировать свою программу. Я просто пытаюсь выяснить, правильно ли он сформирован в соответствии со стандартом С++ и почему два протестированных мной компилятора ведут себя по-разному.


person Supremum    schedule 04.07.2015    source источник
comment
Название вопроса сбивает с толку. X::A и Y::A — это два разных типа; нет предварительного объявления и последующего определения.   -  person Kerrek SB    schedule 04.07.2015
comment
Спасибо за предложение. Сейчас я изменил название.   -  person Supremum    schedule 04.07.2015
comment
Вы пробовали using ::X::A?   -  person bweber    schedule 04.07.2015
comment
Интересный вопрос заключается в том, можете ли вы определить X::A a;. Это должно быть неправильно сформировано, так как X::A неполное. Возможно, GCC ошибается, отождествляя определение Y::A с определением X::A. Clang отвергает этот код.   -  person Kerrek SB    schedule 04.07.2015
comment
Будет еще интереснее, если вы также добавите enum A { FOO }; в пространство имен Y. Теперь Clang его компилирует, а GCC жалуется, что ранее было объявлено A.   -  person Kerrek SB    schedule 04.07.2015
comment
Каррек СБ: Интересно. Кажется, это говорит о том, что в этом случае компилятор не согласен с тем, что если class A; и класс А {}; объявляет два независимых объекта или если первый является просто предварительным объявлением второго. Это двусмысленность в стандарте или ошибка компилятора?   -  person Supremum    schedule 04.07.2015
comment
@Supremum: я не уверен. Я думаю, что в стандарте ясно сказано об этом, но удивительно, что два крупных компилятора ошибаются двумя разными способами, так что, возможно, я неправильно читаю стандарт. Я жду, когда кто-нибудь предложит несогласный ответ.   -  person Kerrek SB    schedule 04.07.2015
comment
@Karrek SB: Да, я думаю, ты прав. Я не вижу в стандарте ничего, разрешающего повторную декларацию через декларацию использования. Они кажутся непрозрачными в этом отношении. Но было бы хорошо, если бы кто-то это подтвердил.   -  person Supremum    schedule 04.07.2015
comment
@Karrek SB: Используя глобальное пространство имен вместо пространства имен X, мы получаем программу, которая, если вы правы, сформирована, но компилируется как минимум тремя большими компиляторами: gcc, clang ( melpon.org/wandbox/permlink/VFwckBYpyhILFaJQ) и Visual C++ ( webcompiler.cloudapp.net ).   -  person Supremum    schedule 04.07.2015
comment
Сообщите об этом и в GCC :-)   -  person Kerrek SB    schedule 06.07.2015
comment
Да, я хочу, я просто хотел, чтобы это было сначала подтверждено для clang.   -  person Supremum    schedule 06.07.2015
comment
@Supremum: сопровождающий GCC говорит, что кто-то должен сообщить об этом. Пожалуйста, сообщите об этом :-)   -  person Kerrek SB    schedule 06.07.2015
comment
Керрек С.Б.: Я сообщил об аналогичной ошибке в gcc: gcc.gnu.org /bugzilla/show_bug.cgi?id=66879   -  person Supremum    schedule 15.07.2015
comment
хорошая работа найти это. удивительно, как после 25 лет истории компиляторы все еще могут получать неясные результаты для таких маленьких программ.   -  person v.oddou    schedule 16.07.2015


Ответы (2)


Я считаю, что программа плохо сформирована. [basic.scope.declarative]/4 говорит:

Учитывая набор объявлений в одной декларативной области, каждое из которых указывает одно и то же неполное имя,

- все они должны ссылаться на один и тот же объект или все должны ссылаться на функции и шаблоны функций; или

- ровно одно объявление должно объявлять имя класса или имя перечисления, которое не является именем typedef, а все остальные объявления должны ссылаться на одну и ту же переменную или перечислитель или все ссылаться на функции и шаблоны функций; в этом случае имя класса или имя перечисления скрыто

Два объявления неполного имени A относятся к разным объектам, оба из которых являются классами.

(Интересно, что ни GCC 6.0, ни Clang 3.7, похоже, не диагностируют его таким образом. Оба принимают код в том виде, в котором он написан (не диагностируя объявление двух разных классов с одинаковым именем). Если вы добавите X::A a; в тело main, тогда Clang жалуется о неполном типе X::A.)

person Kerrek SB    schedule 04.07.2015
comment
Разве объявления не относятся к классу A; и class A {} находятся в двух разных декларативных областях? - person Supremum; 04.07.2015
comment
using не является декларацией - person StenSoft; 04.07.2015
comment
Класс объявления А; производится в декларативной области { class A; } и объявление класса A {}; делается в декларативной области { using X::A; класс А {}; }. - person Supremum; 04.07.2015
comment
используя X::A; также является декларацией. Возможно, вы имели в виду два объявления, используя X::A; и класс А {}; Они находятся в одной и той же декларативной области, но действительно ли они относятся к разным сущностям? Является ли стандарт ясным о погоде или нет класса A {}; является переобъявлением класса A; ? - person Supremum; 04.07.2015
comment
Допустим, это нарушение [basic.scope.declarative]/4. Диагностика тогда не нужна? В этом случае это ошибка в GCC и Clang. - person Supremum; 04.07.2015
comment
@StenSoft: Действительно, using - это не объявление, а ключевое слово. Однако он может быть частью using-declaration, которое действительно является объявлением. - person Kerrek SB; 04.07.2015
comment
Я сообщил об этом как об ошибке в clang: llvm.org/bugs/show_bug.cgi? идентификатор = 24030 - person Supremum; 05.07.2015
comment
Ваш цитируемый текст является ODR? - person v.oddou; 16.07.2015

Не совсем уверен, но вы можете попробовать что-то вроде этого:

namespace X { class A; }

namespace Y 
{
  class X::A {}; 
}

int main() 
{
  return 0;
}
person SBNamikaze    schedule 04.07.2015
comment
Предлагаемая программа не соответствует стандарту C++ (нарушает 7.3.1.2 п. 2). См. eel.is/c++draft/namespace.memdef#2. Он не компилируется на gcc и не компилируется на Visual C++. Также я не пытаюсь компилировать свою программу. Я просто пытаюсь выяснить, правильно ли он сформирован в соответствии со стандартом С++ и почему два протестированных мной компилятора ведут себя по-разному. - person Supremum; 04.07.2015