After reading the accepted answer from this question, I thought I understood why the program failed, since the using directive does not actually declare the entity i
in the region. However names introduced by a using declaration can be used just like any other name and acts like a declaration.
With GCC, this fails
#include <iostream>
namespace X { int i = 1; }
using X::i;
int main() {
extern int i;
i = 2;
std::cout<<i;
}
But this is accepted
#include <iostream>
int i;
int main() {
extern int i;
i = 2;
std::cout<<i;
}
Technically, the example you've given does compile, but it fails to link. The issue is the line
extern int i;
What you're telling the compiler/linker here is "there will be a variable i
defined somewhere else in the program, so, compiler, don't worry if you can't find the definition. Linker, I expect you to find the definition of i
once you have all of the object files and link that in here."
We can see this in action using compiler explorer:
Without the extern declaration
In the second case, the declaration of i
"shadows" the X::i
which is visible at global scope, so we get the instruction
mov DWORD PTR i[rip], 2
whereas without the extern declaration, we get
mov DWORD PTR X::i[rip], 2
though I'm not totally sure on the shadowing part, as neither gcc nor clang warns about that with -Wshadow
. In any case, we see now why the second example fails to link: the linker is trying to find the definition of i
to use here, and while X::i
is defined at global scope, i
is not.