Программа с цепочкой использования-деклараций компилируется на MSVS и clang, но не на GCC
Является ли следующая программа хорошо сформированной или плохо сформированной в соответствии со стандартом С++?
namespace X { int i; }
namespace Y { using X::i; }
int main() { using X::i; using Y::i; }
Я получаю разные результаты с разными компиляторами:
Я не хочу исправлять эту программу, чтобы она скомпилировалась на GCC. Я просто хочу знать, что говорит об этом стандарт С++ и почему три компилятора ведут себя по-разному. Также я хочу, чтобы это было результатом ошибки в любом из этих компиляторов.
Ответы
Ответ 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 году) заключается в том, что оспариваемый комментарий на самом деле ошибочен и что этот вопросный код, где два объявления в том же декларативном регионе относятся к одному и тому же сущности, является действительным и должны быть приняты всеми компиляторами.