Consider the following code:
namespace foo {
namespace bar {
class foo {};
}
class baz {};
}
using namespace foo::bar;
::foo::baz mybaz;
Is this code valid?
Or is ::foo
ambiguous?
Or does ::foo
refer to the class foo
, such that there is no ::foo::baz
.
When it comes to compilers, gcc 6.1.1
seems to think the latter:
scope.cpp:9:8: error: ‘baz’ in ‘class foo::bar::foo’ does not name a type
::foo::baz mybaz;
^~~
On the other hand gcc 5.3.1
, clang 3.8.0
, and the intel compiler 16.0.3 do not yield any warnings or errors.
I suspect that under 3.4.3.2.2 of the C++14 standard, this should be valid and not ambiguous, but I am not quite sure.
Edit: In addition, for foo::baz mybaz
only clang reports an ambiguous error.
Global scope or global namespace scope is the outermost namespace scope of a program, in which objects, functions, types and templates can be defined. A name has global namespace scope if the identifier's declaration appears outside of all blocks, namespaces, and classes.
A scope resolution operator without a scope qualifier refers to the global namespace. You can use the scope resolution operator to identify a member of a namespace , or to identify a namespace that nominates the member's namespace in a using directive.
Scope resolution operator in C++ For example, if you have a global variable of name my_var and a local variable of name my_var, to access global my_var, you'll need to use the scope resolution operator.
The scope resolution operator is used to reference the global variable or member function that is out of scope. Therefore, we use the scope resolution operator to access the hidden variable or function of a program.
[namespace.qual] is fairly straightforward:
1 If the nested-name-specifier of a qualified-id nominates a namespace (including the case where the nested-name-specifier is
::
, i.e., nominating the global namespace), the name specified after the nested-name-specifier is looked up in the scope of the namespace. The names in a template-argument of a template-id are looked up in the context in which the entire postfix-expression occurs.2 For a namespace
X
and namem
, the namespace-qualified lookup set S(X
,m
) is defined as follows: Let S0(X
,m
) be the set of all declarations ofm
inX
and the inline namespace set ofX
(7.3.1). If S0(X
,m
) is not empty, S(X
,m
) is S0(X
,m
); otherwise, S(X
,m
) is the union of S(Ni,m
) for all namespaces Ni nominated by using-directives inX
and its inline namespace set.3 Given
X::m
(whereX
is a user-declared namespace), or given::m
(whereX
is the global namespace), if S(X
,m
) is the empty set, the program is ill-formed. Otherwise, if S(X
,m
) has exactly one member, or if the context of the reference is a using-declaration (7.3.3), S(X
,m
) is the required set of declarations ofm
. Otherwise if the use ofm
is not one that allows a unique declaration to be chosen from S(X
,m
), the program is ill-formed.
::foo
is a qualified-id with nested-name-specifier ::
, so the name foo
is looked up in global scope.
S0(::
,foo
) contains the single declaration for namespace foo
since there are no other declarations of foo
in the namespace, and it has no inline namespaces. Since S0(::
,foo
) is not empty, S(::
,foo
) is S0(::
,foo
). (Note that since S0(::
,foo
) is not empty, namespaces nominated by using directives are never examined.)
Since S(::
,foo
) has exactly one element, that declaration is used for ::foo
.
The name ::foo::baz
is therefore a qualified-id with nested-name-specifier ::foo
that nominates a namespace. There is again a single declaration of baz
in namespace ::foo
, so the name ::foo::baz
refers to the class baz
declaration.
The behavior you observe in GCC 6+ is in fact a bug, filed as GCC PR 71173.
EDIT: Lookup of foo::baz
when it appears at global scope, a la:
foo::baz bang;
first requires a lookup of foo
as an unqualified name. [basic.lookup.qual]/1 says this lookup only sees "namespaces, types, and templates whose specializations are types." [basic.scope.namespace]/1 tells us about namespace members and their scopes:
The declarative region of a namespace-definition is its namespace-body. Entities declared in a namespace-body are said to be members of the namespace, and names introduced by these declarations into the declarative region of the namespace are said to be member names of the namespace. A namespace member name has namespace scope. Its potential scope includes its namespace from the name’s point of declaration onwards; and for each using-directive that nominates the member’s namespace, the member’s potential scope includes that portion of the potential scope of the using-directive that follows the member’s point of declaration.
[basic.lookup.unqual]/4 tells us that the declaration of namespace foo
is visible:
A name used in global scope, outside of any function, class or user-declared namespace, shall be declared before its use in global scope.
and para 2 says that class foo
is also visible here:
The declarations from the namespace nominated by a using-directive become visible in a namespace enclosing the using-directive; see 7.3.4. For the purpose of the unqualified name lookup rules described in 3.4.1, the declarations from the namespace nominated by the using-directive are considered members of that enclosing namespace.
Since two declarations of different foo
entities from differing namespaces are found, [namespace.udir]/6 tells us the use of foo
is ill-formed:
If name lookup finds a declaration for a name in two different namespaces, and the declarations do not declare the same entity and do not declare functions, the use of the name is ill-formed.
Our name lookup for foo::baz
dies before ever getting to baz
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With