I understand that only data members which are static, const and int/enum (pre c++11) can be initialized inside the class declaration. "All other static data members must be defined at global namespace scope (i.e. outside the body of the class definition) and can be only initialized in those definitions".
Why can't other static data members be initialized in the class definition? Was there a specific reason this was forbidden?
If the data members are specific to the class, why are they declared at the global namespace scope and not some scope relevant to their class?
Most likely because C++ has separate translation units. The compiler needs to pick an object file where the initialization logic for those symbols will be placed. Forcing this to be in a specific source file makes that decision easy for the compiler.
Because that's just how C++ does class members. This is no different than other class members like member functions:
Header file:
Source file:
There's a specific exception to allow static const integral members to be initialized in the header because it allows the compiler to treat them as compile-time constants. That is, you can use them to define sizes of arrays or other similar bits in the class definition.
In general, all static objects require a definition, in one single translation unit, so that they have a well-defined address. As a special exception, static, constant, non-volatile class members don't need a definition if their address is not required, and they have a simple enough type that their value can be replaced by a compile-time constant.
Historically, "simple enough" was defined as an integral or enumeration type; C++11 extends that to include any literal type with a
constexpr
specifier.They are not declared at the global namespace scope. They are declared and scoped within the class.
If you mean, why are they defined outside the class definition, that's because there must be only one definition of the static member in the whole program; but the class must be defined in each translation unit that uses it.
They would be re-initialized every time the class was instantiated. Every time you create a new object of type Foo, the static variables for all Foos would be reset to their initial value, which is probably not what you want. Therefore, if you want to use static variables with your object, they either a) can't change their value, meaning that reinitializing them to the same value is safe, or b) can only be changed outside the context of an initializer function.
You have to look at it the other way around. Basically, static data members must be defined and initialized outside the class definition, in a source file. There's an exception for
static const int
because it avoids various ugly workarounds for defining the size of a member array.A static data member is in many respects (and especially from the point of view of a compiler) similar to a namespace-scope data object with external linkage.
The declaration of a static data member is just a declaration, not a definition. It is similar to an
extern
declaration of a global object and must be included into any translation unit where the object may be used.The definition must appear in exactly one translation unit and this is where the initializer expression belongs. Unless an expression fulfills the strict criteria of a constant expression, its value may well depend upon the time and context it is called. Having such an initializer expression occur in multiple translation units would make the execution context and time of the initialization and finally the initial value ambiguous.
A class-scoped compile-time constant was deemed sufficiently valuable to make an exception for certain kinds of constant static members (which then could be used to initialize enums or specify array dimensions, etc). With constant expressions it is at least more difficult to accidentally incur different initializer values in different translation units. This concept was extended in C++11 with
constexpr
members.The declaration is within the class scope. The non-definition declaration is literally within the class definition and the definition appears at namespace scope, just like any other out-of-class definition of a class member. The member name is qualified by the class name, so it is clearly denoted as a member of the class and the initializer expression is actually considered to be within the scope of the class (at least in C++11; I have no C++98/03 standard available here).