Scope of the c++ using directive
-
03-07-2021 - |
Question
From section 7.3.4.2 of the c++11 standard:
A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup (3.4.1), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace. [ Note: In this context, “contains” means “contains directly or indirectly”. —end note ]
What do the second and third sentences mean exactly? Please give example.
Here is the code I am attempting to understand:
namespace A
{
int i = 7;
}
namespace B
{
using namespace A;
int i = i + 11;
}
int main(int argc, char * argv[])
{
std::cout << A::i << " " << B::i << std::endl;
return 0;
}
It print "7 7" and not "7 18" as I would expect.
Sorry for the typo, the program actually prints "7 11".
Solution
Eliminating the undefined behaviour:
namespace A
{
int i = 7;
}
namespace B
{
using namespace A;
int tmp = i + 11;
int i = tmp;
}
#include <iostream>
int main()
{
std::cout << A::i << " " << B::i << std::endl;
return 0;
}
The meaning of the standard is that at the line
int tmp = i + 11;
the name i
appears in the "nearest enclosing namespace which contains both the using-directive and the nominated namespace"; the using-directive appears in namespace B
while the nominated namespace is namespace A
; the nearest enclosing namespace is the global namespace, so i
appears as ::i
. This means that if a name i
is already present in the global namespace the code is ambiguous.
For a more complex example:
namespace A {
namespace B {
namespace C {
int i = 4;
}
}
namespace D {
using namespace B::C;
namespace E {
int j = i;
}
}
}
At the line int j = i
, i
appears in the nearest enclosing namespace of the using-directive (i.e., A::D
) and the nominated namespace (A::B::C
), which is A
. So, within A::D
after the using-directive, and so also within A::D::E
, the unqualified name i
can refer to A::B::C::i
appearing as A::i
, shadowing any ::i
, conflicting with any A::i
, and being shadowed by any A::D::i
or A::D::E::i
(within A::D::E
):
int i = 1; // shadowed by A::B::C::i appearing as A::i
namespace A {
int i = 2; // conflicts with A::B::C::i appearing as A::i
namespace B {
int i = 3; // irrelevant
namespace C {
int i = 4; // nominated; appears as A::i
}
}
namespace D {
int i = 5; // shadows A::B::C::i appearing as A::i
using namespace B::C;
namespace E {
int i = 6; // shadows A::B::C::i appearing as A::i
int j = i;
}
}
}
Note that just because the name appears as A::i
during unqualified name lookup, that does not mean that it actually is there; the qualified name A::i
will continue to refer only to an actual name A::i
(if any exists).
OTHER TIPS
The using
statement in your code is irrelevant. B::i
is already in scope when the initializer for B::i
is evaluated. You can trivially prove this by deleting the using
statement; your code should compile and run just the same. In any case, the value of B::i
ends up being undefined because it depends on an uninitialized value (i.e. the value that B::i
had when the initializer was evaluated).