Ответ 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
.