Why do non-const, non-int/enum static data members

2020-02-11 04:59发布

问题:

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?

回答1:

Why can't other static data members be initialized in the class definition? Was there a specific reason this was forbidden?

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.

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?

Because that's just how C++ does class members. This is no different than other class members like member functions:

Header file:

namespace example {

// Class declared in header
struct some_class
{
    // Member variable
    static float example;
    // Member function
    void DoStuff() const;
};

}

Source file:

namespace example {

    // Implement member variable
    float some_class::example = 3.14159;
    // Implement member function
    void some_class::DoStuff() const
    {
         //....
    }
}

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.



回答2:

Why can't other static data members be initialized in the class definition? Was there a specific reason this was forbidden?

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.

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?

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.



回答3:

Why can't other static data members be initialized in the class definition? Was there a specific reason this was forbidden?

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.

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?

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).



回答4:

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.



回答5:

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.