You don't have to specify an initializer for mySecondVar
at the point of declaration. Nor is the initializer required to be constexpr
itself.
This means that if we attempt to define myFirstVar
like this:
class MyClass {
static constexpr int myFirstVar;
};
int MyClass::myFirstVar = 1;
Or like this:
#include <cstdlib>
class MyClass {
static constexpr int myFirstVar = rand();
};
It's ill-formed either way. constexpr
semantics demand it and for a good reason.
The inline
specifier approach allows us to include a static variable definition in the header itself, without the initializer being constexpr
; or if the initializer is fairly complex it doesn't have to be in the class definition itself.
So this is a perfectly valid header in C++17:
#include <cstdlib>
class MyClass {
static const int mySecondVar;
};
inline const int MyClass::mySecondVar = rand();
The standard promises us that all translation units that include the header will see the same value for the variable, even though we won't know what it is until run-time.
It's mostly a library writers tool. Assume your library is header only. Then in the olden days, what were your options if you needed a static constant defined like this?
Well, you could have an object file shipped with your library. It will be compiled from a translation unit that contains just the constant definition. Now the library isn't header-only.
Or you could rely on inline functions instead. The inline variable effect can be achieved with the following:
class MyClass {
static inline int mySecondVar();
};
inline int MyClass::mySecondVar() {
static const int value = rand();
return value;
}
But it's hidden behind a wall of syntax, and masks what is essentially a constant, with a function call operator.
Without inline
, it's explicitly stated as only a declaration. As specified in [class.static.data]/2
The declaration of a non-inline static data member in its class
definition is not a definition and may be of an incomplete type other
than cv void. The definition for a static data member that is not
defined inline in the class definition shall appear in a namespace
scope enclosing the member's class definition.
The rationale is most probably to keep legacy code intact and valid. Recall that we could initialize integral constants in the class definition itself since about forever. But odr-using them still required an out-of-class definition in some translation unit.
So to makes such variables implicitly inline could be problematic in existing codebases. The committee is always thinking about backwards compatibility when core language features are added.
For instance, consider this valid C++03 class definition:
struct foo {
static const int n = 3;
double bar[n];
};
n
can be used as a constant expression to define the extent of bar
, and it's not considered an odr-use. Nowadays we'd write it as constexpr
1, however that above is still valid. But there may be cases were n
would have to be odr-used (imagine its address taken, or a reference bound to it, etc). They are probably not many, and probably not common, but certain API's have crazy requirements that would end up necessitating this
const int foo::n;
to appear in some translation unit.
Now, if static inline int i = 8;
was suddenly implicitly inline
, the definition above (that is in an existing code base) would be an odr-violation. Now previously well-formed code, is ill-formed. So it's best to allow only explicit inline
to take effect here, since only new code will actually have it.
1 One could argue that static constexpr
variables may have the same issue (and yet they are implicitly inline). But IIRC their original wording allowed this change without potentially breaking existing code. It was essentially already "inline" by everything but name.
Best Answer
The point here is that
constexpr int x = 1;
at namespace scope has internal linkage in C++14.If you make it implicitly inline without changing the internal linkage part, the change would have no effect, because the internal linkage means that it can't be defined in other translation units anyway. And it harms teachability, because we want things like
inline constexpr int x = 1;
to get external linkage by default (the whole point of inline, after all, is to permit the same variable to be defined in multiple translation units).If you make it implicitly inline with external linkage, then you break existing code:
This perfectly valid C++14 would become an ODR violation.