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

Разрешение глобальной области при наличии пространства имен

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

namespace foo {
  namespace bar {
    class foo {};
  }
  class baz {};
}
using namespace foo::bar;

::foo::baz mybaz;

Является ли этот код действительным? Или ::foo неоднозначный? Или ::foo ссылается на class foo, так что нет ::foo::baz.

Когда дело доходит до компиляторов, gcc 6.1.1 кажется, думает последнее:

scope.cpp:9:8: error: ‘baz’ in ‘class foo::bar::foo’ does not name a type
 ::foo::baz mybaz;
        ^~~

С другой стороны, gcc 5.3.1, clang 3.8.0, а компилятор Intel 16.0.3 не дает никаких предупреждений или ошибок.

Я подозреваю, что в соответствии с 3.4.3.2.2 стандарта С++ 14 это должно быть допустимым, а не двусмысленным, но я не совсем уверен.

Изменить: кроме того, для foo::baz mybaz только clang сообщает о неоднозначной ошибке.

4b9b3361

Ответ 1

[namespace.qual] довольно прост:

1 Если спецификатор вложенного имени квалифицированного идентификатора назначает пространство имен (включая случай, когда спецификатор вложенного имени ::, т.е. назначение глобального пространства имен), имя, указанное после вложенных имен, имя-спецификатор просматривается в области пространства имен. Имена в шаблоне-аргументе идентификатора шаблона просматриваются в контексте, в котором происходит все постфиксное выражение.

2 Для пространства имен X и имени m, набор для поиска по именам S (X, m) определяется следующим образом: Пусть S 0 (X, m) - это множество всех объявлений m в X и встроенного пространства имен в X (7.3.1). Если S 0 (X, m) не пусто, S (X, m) является S 0 (X, m); в противном случае S (X, m) является объединением S (N i, m) для всех пространств имен N i, назначенных с помощью директив в X и его встроенное пространство имен.

3 Учитывая X::m (где X - объявленное пользователем пространство имен) или задано ::m (где X - глобальное пространство имен), если S (X, m) - это пустой набор, программа плохо сформирована. В противном случае, если S (X, m) имеет ровно один член, или если контекст ссылки является использованием-декларации (7.3.3), S (X, m) является обязательным набором деклараций m. В противном случае, если использование m не является тем, которое позволяет выбрать уникальное объявление из S (X, m), программа плохо сформирована.

::foo является квалифицированным идентификатором с вложенным именем-спецификатором ::, поэтому имя foo просматривается в глобальной области.

S 0 (::, foo) содержит единственное объявление для namespace foo, поскольку в пространстве имен нет других объявлений foo, и он не имеет встроенных пространств имен. Поскольку S 0 (::, foo) не пусто, S (::, foo) является S 0 (::, foo). (Заметим, что поскольку S 0 (::, foo) не пуст, пространства имен, назначаемые с помощью директив, никогда не рассматриваются.)

Так как S (::, foo) имеет ровно один элемент, это объявление используется для ::foo.

Таким образом, имя ::foo::baz является идентификатором с идентификатором-вложенным именем ::foo, который назначает пространство имен. В пространстве имен ::foo снова появляется одно объявление baz, поэтому имя ::foo::baz относится к объявлению class baz.

Поведение, которое вы наблюдаете в GCC 6+, на самом деле является ошибкой, поданной как GCC PR 71173.

РЕДАКТИРОВАТЬ: поиск foo::baz, когда он отображается в глобальном масштабе, a la:

foo::baz bang;

сначала требуется поиск foo в качестве неквалифицированного имени. [basic.lookup.qual]/1 говорит, что этот поиск видит только "пространства имен, типы и шаблоны, специализация которых - это типы". [basic.scope.namespace]/1 рассказывает нам о членах пространства имен и их областях:

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

[basic.lookup.unqual]/4 говорит нам, что объявление namespace foo видно:

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

и пара 2 говорят, что class foo также видна здесь:

Объявления из пространства имен, назначаемого с помощью директивы using, становятся видимыми в пространстве имен, включающем директиву using; см. 7.3.4. Для целей правил поиска неквалифицированных имен, описанных в 3.4.1, объявления из пространства имен, назначенного с помощью директивы use, считаются членами этого охватывающего пространства имен.

Поскольку найдено два объявления разных объектов foo из разных пространств имен, [namespace.udir]/6 говорит нам, что использование foo плохо сформировано:

Если поиск имени находит объявление для имени в двух разных пространствах имен, и объявления не объявляют один и тот же объект и не объявляют функции, использование имени плохо сформировано.

Наш поиск имени для foo::baz умирает до того, как он дойдет до baz.