c++classstatic

How do non-inline static data member behave in terms of declaration and definition?


We have the fact that a non-inline static data member in a class definition is a non-defining declaration, draft source.

We also have that a non-volatile non-inline const static data member of integral or enumeration type can specify a initializer, draft source

Phrasing it here:

if a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression ([expr.const]). The member shall still be defined in a namespace scope if it is odr-used ([basic.def.odr]) in the program and the namespace scope definition shall not contain an initializer.

So I would presume the following is a valid code:

class A {
public:
   static const int a = 1;
};
const int A::a = 3;

The declaration inside of the class definition is not a definition because, again, its a declaration of a non-inline static data member in a class definition.

In Visual Studio the code above retuns me a compilation error:

error C2374: 'a': redefinition; multiple initialization

Questions: What is the practical meaning of a non defining declaration with initializer? Why do the code above is not valid since we only have one definition?

Obs: probably subtle situation here because const I would presume, but the redefinition error is what is getting my attention


Solution

  • The error message you're getting is a bit deceiving--it's not really a duplicate definition--but only one of the two can initialize a. So you can have either:

    class A {
    public:
       static const int a = 1;
    };
    const int A::a;
    

    ...or:

    class A {
    public:
       static const int a;
    };
    const int A::a = 3;
    

    ...but you can't have initializers in both places.

    Also note that since it's a const, you can usually just use:

    class A {
    public:
       static const int a = 1;
    };
    

    ...and as long as you don't use it in a way that requires it to have an address (e.g., take its address or pass it by reference), you don't need an actual definition at all. In this case, it's pretty much equivalent to #define (you can read the name, but to the compiler it's pretty much just a number)--except that unlike #define, it does respect scope and such.