I have a few questions about the linkage from the following variables. By examples of 7.1.1/7 of C++03 and experimenting with compilers (Comeau, Clang and GCC), I came to the following linkage kinds:
First
static
, thenextern
static int a; // (a) extern int a; // (b) valid, 'a' still internal
It's clear to me with accordance to section 3.5: (a) implies internal linkage. And (b) also implies internal linkage, because the name "a" is declared static (by (a)).
First
extern
, thenstatic
extern int b; // (c) static int b; // (d) invalid!
First, (c) implies external linkage. But (d) implies internal linkage because the name "b" is declared static by (d). This is invalid according to 7.1.1/7, since the linkage implied is not consistent.
First
const
, thenextern
const double pi1 = 3.14; // (e) extern const double pi1; // (f) valid and 'pi1' is internal
First, (e) implies internal linkage, because it is const, and neither declared explicit extern nor previously implied external linkage. And (f) should imply extern linkage and be an error, because it explicitly declares the name extern, but the compilers keep it internal! Why so? That's my question.
First
extern
, thenconst
extern const double pi2; // (g) const double pi2 = 3.14; // (h) valid and 'pi2' is external
Now, (g) implies external linkage because we explicitly declared extern. And (h) also implies external linkage because (g) explicitly declared extern.
I have experimentally found out the linkage for 3 and 4 with the following template (the second argument is required to have external linkage)
template<typename T, T&> struct ensure { };
ensure<const double, pi1> e1; // failed
ensure<const double, pi2> e2; // succeeded
Summary: The Discussion with Charles Bailey turned out to be quite fruitful and showed there are two possible interpretations of 3.5/3
, where the important bullet point reads
A name having namespace scope (3.3.5) has internal linkage if it is the name of
- an object or reference that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage;
If we look at point (f)
, then the two interpretations come to different conclusions, as shown below
The first interpretation notes that
pi1
is declaredconst
but is also declaredextern
. The variable has thus external linkage.The second interpretation interpretes both occurences of "declared" to refer to the same declaration. In this way, it means that it is declared
const
, but notextern const
. We note that(e)
is declaredconst
and notextern const
, thus we givepi1
internal linkage.
Now what interpretation is correct? I can't determine from that wording, but compilers seem to interpret this the second way. In particular, if we take the first interpretation, then the last quoted part of 3.5/3
would be superfluous, because there would be no valid scenario in which a name would be declared const
and previously declared with external linkage but without an explicit extern
.
I think in #3 you've made an error in your analysis. As far as I know,
const
does not imply anything about linkage. I'm not sure how you're coming to the conclusion that the compiler makes the linkage internal. Most compilers will (as an optimization) replace all references to the const variable by the value it's been initialized to, so the symbol may not appear at all in the code.And even if you didn't, it's clear from #1 that if something with internal linkage is subsequently declared with the
extern
keyword that it remains with internal linkage. So I don't know why you would expect an error.And if
const
implied internal linkage, then #4 should be an error for the same reason #2 is.My interpretation is as follows. When considering the linkage of a name we consider previous declarations as well as the one being interpreted at this point in the parse. This is why
static int a; extern int a;
is OK, butextern int b; static int b;
is not.On encountering the first declaration we note that
pi1
is explicitly declaredconst
but neither explicitly declaredextern
nor previously declared to have external linkage. This matches one of the options of 3.5/2 thereforepi1
has internal linkage.On encountering the second declaration we ask is
pi1
the name of an object that is explicitly declaredconst
but neither explicitly declaredextern
nor [... blah ...]. I contend that it is because it was so declared at point (e). Sure, it isn't declared that way everywhere but in the same waya
was the name of an object declaredstatic
when we were considering theextern int a;
declaration even though it wasn't declaredstatic
everywhere. This, to me, means that the declaration (f) doesn't imply a different linkage from declaration (e).Having both (e) and (f) in the same namespace scope is simply invalid, by §7.1.1/7 "The linkages implied by successive declarations for a given entity shall agree.".
This rule requires a diagnostic.
However, at least Comeau Online does not diagnose the violation.
Cheers & hth.,
EDIT: He he, I looked up DR 426, as mentioned in another answer here, and it seems those who drafted the proposed resolution, making it UB instead of diagnosable, were not aware of §7.1.1/7. I'm not going to comment on the issue or even raise it in comp.std.c++ because I found the standardization work to be far too political and nonsensical (mumbo-jumbo arguments) for me. But either way, the code's not valid.