Let's say I have the following setup in a C++ program, in global/namespace scope (outside of any block):
Situation 1:
a.cpp
extern const int i=5;
b.cpp
extern const int i;
There are two ways, in my mind, that the compiler could see this. One is that the true declaration of i is in b.cpp, while a.cpp has merely a forward declaration and the initialization. (Since we know that for specifically extern consts, initialization at the time of declaration is not required) The other possible executable the compiler could generate would have a.cpp containing the true declaration, with initialization, and the statement in b.cpp is seen as a forward declaration, merely required to be there to assist the compiler in knowing what i is.
How does the compiler decide which file is responsible for the actual declaration, and therefore memory allocation associated with i? This could have outwardly observable effects if, instead of int, a type with an interesting constructor were used.
How does the answer to this question change, if at all, with:
situation 2:
a.cpp
extern const int i;
extern const int i=5;
b.cpp
extern const int i;
Presence of an initializer immediately turns a declaration into a definition (with few exceptions, irrelevant in our context). Which means that
extern const int i = 5;
is a definition of your i
. It defines i
and gives it external linkage, i.e. creates the actual i
and makes it visible to other translation units ("exports" it).
Meanwhile,
extern const int i;
is a non-defining declaration if i
. It basically says that i
is defined elsewhere ("imports" it).
When you for some reason need a global const
object in C++, the proper explicit application of extern
becomes crucial, since in C++ const
objects have internal linkage by default.
Just keep in mind that in order for a const int
object to be eligible for Integral Constant Expressions (ICE), a declaration of that const int
object with an ICE initializer has to be visible.