A quick reminder, so that it is clear what we are talking about:
int const a; // illegal
int const a = 42; // definition, internal linkage
extern int const a; // declaration, external linkage
extern int const a = 42; // definition , external linkage
Note that without the const
, the first two declarations above are both
definitions with external linkage. This is anything but orthogonal, and
not very intuitive, but this is what the current rules say.
The problem with giving a const external linkage is that there can be
only one definition of an object with external linkage, and with one
exception, only the definition can have an initializer. This means that
for a const with external linkage, the actual value (necessary if the
const is to be used in a constant expression) can only be visible in one
translation unit. This is probably the motivation for giving const
internal linkage by default.
Of course, this causes no end of problems with templates, at least
theoretically; the following header has undefined behavior if it is
including in more than one translation unit:
#include <std::vector>
int const fixedValue = 42;
inline void insertFixedValue( std::vector<int>& dest )
{
dest.push_back( fixedValue );
}
The standard says that not only must inline functions and templates have
an identical sequence of tokens, but that all of the symbols must bind
to the same object in every translation unit, or there is a violation of
the one definition rule. And since fixedValue
does not have external
linkage, there is a unique instance in each translation unit. (There is
an exception if the symbol refers to a const
object and there is
an immediate lvalue to rvalue conversion. Since
std::vector<int>::push_back
takes its argument by reference, however,
there is no immediate lvalue to rvalue conversion, and we get undefined
behavior.)
And of course, anyone with a template:
template <int& r> ...
cannot instantiate it with fixedValue
either.
The reason for the internal linkage is, of course, historical. Today,
compilers must be able to support things like:
struct X
{
static int const a = 42; // declaration!!!, external linkage
};
and all sorts of duplicate definitions for functions. It would be
relatively trivial to extend the rules allowing initializers on a
declaration in a class to variables at namespace scope, to give
something like:
int const a; // illegal
int const a = 42; // definition, external linkage
extern int const a; // declaration, external linkage
extern int const a = 42; // declaration, external linkage
This would restore orthogonality (even if it required extra typing). it
would also break almost all existing code.
Another alternative would be to treat const
variable definitions
exactly as function templates are treated today: you can have multiple
definitions, but they must all be identical. This would probably avoid
most, if not all, code breakage.