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

Программа с цепочкой использования-деклараций компилируется на MSVS и clang, но не на GCC

Является ли следующая программа хорошо сформированной или плохо сформированной в соответствии со стандартом С++?

namespace X { int i; }

namespace Y { using X::i; }

int main() { using X::i; using Y::i; }

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

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

4b9b3361

Ответ 1

Clang и MSVC верны; этот код действителен. Как отмечает Alf, [namespace.udecl] (7.3.3)/10 говорит

Использование-декларация является объявлением и поэтому может использоваться повторно там, где (и только там) разрешено несколько объявлений.

Тем не менее, нет ограничений на множественные объявления одного и того же объекта в области блоков, поэтому исходный пример действителен. Соответствующим случаем, не связанным с использованием-деклараций, является:

int n;
void f() {
  extern int n;
  extern int n;
}

Это допустимо (и принимается GCC, EDG, Clang и MSVC), поэтому (по вышеприведенному правилу) также действителен исходный пример.

Стоит отметить, что пример в [namespace.udecl] (7.3.3)/10 содержит ошибку. В нем говорится:

namespace A {
  int i;
}

void f() {
  using A::i;
  using A::i; // error: double declaration
}

... но комментарий неверен; во второй декларации нет ошибки. См. Обсуждение в основной проблеме 36. Я удалил пример из стандарта, чтобы он не путал больше людей.

Ответ 2

Программа не должна компилироваться, потому что она объявляет X::i дважды в той же области блока.

С++ 14 §7.3.3/10:

" Использование-объявления является декларацией и поэтому может использоваться повторно, где (и только там) несколько объявления разрешены. [Пример:

namespace A {
    int i;
}

namespace A1 {
    using A::i;
    using A::i;           // OK: double declaration
}

void f() {
    using A::i;
    using A::i;           // error: double declaration
}

Изменить: Ненормативный комментарий, приведенный выше, и который, как я думал, ответил на вопрос, был там первоначально на С++ 98 и сохранился через Technical Corrigendum 1 (С++ 03), С++ 11 и С++ 14. Но, по-видимому, это неверно. Ричард Смит в своем ответе цитирует основной вопрос 36 об этом, впервые поднятый Эндрю Кенигом на 2 nd Август 1998 года (меньше чем через месяц после утверждения ANSI первого стандарта), что, по-видимому, означает, что известный неверный комментарий может выдержать три пересмотра стандарта.

Ссылаясь на основную проблему:

Активные проблемы с базовым языком С++, выпуск 36:

" Примечания с 04/00 заседания:
Рабочая группа основного языка не смогла прийти к консенсусу относительно того, какую декларацию следует использовать в декларации использования. В опросе соломы 7 членов высказались за разрешение использования деклараций везде, где может появляться объявление без определения, в то время как 4 предпочли разрешить несколько приемов использования только в области пространства имен (обоснование заключается в том, что разрешение для нескольких объявлений-объявлений в первую очередь поддерживает его использование в нескольких файлах заголовков, которые редко включаются в любую область, кроме области пространства имен). Джон Спайсер отметил, что объявления friend могут появляться несколько раз в области видимости класса и спрашивают, будет ли использование-деклараций иметь такое же свойство в соответствии с разрешением" как объявление ".

В результате отсутствия согласия проблема была возвращена в статус" открыто".

Общее обсуждение нескольких деклараций с тем же именем приведено в п. 3.3.1/4 как в С++ 98, так и в С++ 14. Насколько я вижу, текст С++ 14 дословно идентичен исходному тексту С++ 98. И сам по себе он позволяет объявлять одно и то же имя несколько раз в той же декларативной области в ряде случаев, одна из которых заключается в том, что все объявления относятся к одному и тому же объекту:

С++ 14 §3.3.1/4:

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

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

  • ровно одно объявление должно объявлять имя класса или имя перечисления, которое не является типептическим именем и другие объявления должны относиться к одной и той же переменной или перечислителю, или все относятся к функциям и шаблоны функций; в этом случае имя класса или имя перечисления скрыты (3.3.10). [Примечание: A имя пространства имен или имя шаблона класса должны быть уникальными в декларативном регионе (7.3.2, раздел 14). -end note]

Однако формулировка здесь говорит только о том, что прямо недействительно. Декларация может быть запрещена другими правилами, даже если она не запрещена этим. Например, существует такое ограничение для объявлений членов класса:

С++ 14 §9.2/1:

" [& hellip;] Член не должен быть дважды объявлен в членстве, спецификацию, за исключением того, что шаблон вложенного класса или класса участника может быть объявлен, а затем определен позднее, и за исключением того, что перечисление может быть введено с непрозрачной-перечислением-декларацией и позже обновлено с спецификатором перечисления.

Я не могу найти такое ограничение, которое поддерживает явно неверный комментарий в С++ 14 §7.3.3/10, цитируемый в начале выше, т.е. я не могу найти какой-либо специальной обработки областей блоков или областей пространства имен, и поэтому предварительный вывод (с учетом выживаемости комментариев, несмотря на то, что он был оспариван уже в 1998 году) заключается в том, что оспариваемый комментарий на самом деле ошибочен и что этот вопросный код, где два объявления в том же декларативном регионе относятся к одному и тому же сущности, является действительным и должны быть приняты всеми компиляторами.