Why Does a Static Data Member Need to Be Defined Outside of the Class

Why does a static data member need to be defined outside of the class?

It's a rule of the language, known as the One Definition Rule. Within a program, each static object (if it's used) must be defined once, and only once.

Class definitions typically go in header files, included in multiple translation units (i.e. from multiple source files). If the static object's declaration in the header were a definition, then you'd end up with multiple definitions, one in each unit that includes the header, which would break the rule. So instead, it's not a definition, and you must provide exactly one definition somewhere else.

In principle, the language could do what it does with inline functions, allowing multiple definitions to be consolidated into a single one. But it doesn't, so we're stuck with this rule.

Why C++ static data members are needed to define but non-static data members do not?

When you declare something, you're telling the compiler that the name being declared exists and what kind of name it is (type, variable, function, etc.) The definition could be with the declaration (as with your class A) or be elsewhere—the compiler and linker will have to connect the two later.

The key point of a variable or function definition is that it tells the compiler and linker where this variable/function will live. If you have a variable, there needs to be a place in memory for it. If you have a function, there needs to be a place in the binary containing the function's instructions.

For non-static data members, the declaration is also the definition. That is, you're giving them a place to live¹. This place is within each instance of the class. Every time you make a new A object, it comes with an ns as part of it.

Static data members, on the other hand, have no associated object. Without a definition, you've got a situation where you have N instances of A all sharing the same s, but nowhere to put s. Therefore, C++ makes you choose one translation unit for it via a definition, most often the source file that acommpanies that header.

You could argue that the compiler should just pick one instance for it, but this won't work for various reasons, one being that you can use static data members before ever creating an instance, after the last instance is gone, or without having instances at all.

Now you might wonder why the compiler and linker still can't just figure it out on their own, and... that's actually pretty much what happens if you slap an inline on the variable or function. You can end up with multiple definitions, but only one will be chosen.


1: Giving them a place to live is a little beside the point here. All the compiler needs to know when it creates an object of that class is how much space to give it and which parts of that space are which data members. You could think of it as the compiler doing the definition part for you since there's only one place that data member could possibly live.

Why do non-constant static variables need to be initialized outside the class?

Essentially it's because x exists independently of the number of instances of A that are created.

So storage for x needs to be defined somewhere - you can't rely on an instance of A to do that, and that's what

A::x = 0;

in exactly one translation unit, does.

Why static members need to be initialized out of the class

What you call initialization is really a definition.

And the reason is that definitions can only be done once is a single translation unit.

If the static member variable were defined in the header file it could be defined multiple times breaking the one definition rule.

Or the compiler would not know which translation unit to put the definition in, since it doesn't know anything about other possible translation units.

Why do we need to define a static variable of a class, unlike the other member of a class?

For static data member you have to allocate memory for it in your implementation, what you are doing now does not allocate memory but you are just getting the size of the int.
In C++ 17 you can declare static variable inline, for int its default value is zero but you can set any value you want. Like this:

 static inline int b=4;

Should I always define a constexpr static data member outside of the class?

Should I always define a constexpr static data member outside of the class?

Always?! Only a Sith deals in absolutes.

C++17 made static constexpr variables implicitly inline (in the linkage sense, they always needed an in-class initializer). The out-of-class definition remains an optional but deprecated feature.

D.1 Redeclaration of static constexpr data members [depr.static_constexpr]

1 For compatibility with prior C++ International Standards, a
constexpr static data member may be redundantly redeclared outside
the class with no initializer. This usage is deprecated. [ Example:

struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};

constexpr int A::n; // redundant declaration (definition in C++ 2014)

 — end example ]

It's still supported, so you don't have to go about updating all of your existing code bases today. But it may be removed in the future (three, six, maybe nine years from now?) So you won't be able to add a redundant declaration forever.

If you write a new piece of code that requires C++17 or newer, then there's no need to add this re-declaration out of some principle.

Why static variable needs to be explicitly defined?

From the beginning of time C++ language, just like C, was built on the principle of independent translation. Each translation unit is compiled by the compiler proper independently, without any knowledge of other translation units. The whole program only comes together later, at linking stage. Linking stage is the earliest stage at which the entire program is seen by linker (it is seen as collection of object files prepared by the compiler proper).

In order to support this principle of independent translation, each entity with external linkage has to be defined in one translation unit, and in only one translation unit. The user is responsible for distributing such entities between different translation units. It is considered a part of user intent, i.e. the user is supposed to decide which translation unit (and object file) will contain each definition.

The same applies to static members of the class. Static members of the class are entities with external linkage. The compiler expects you to define that entity in some translation unit. The whole purpose of this feature is to give you the opportunity to choose that translation unit. The compiler cannot choose it for you. It is, again, a part of your intent, something you have to tell the compiler.

This is no longer as critical as it used to be a while ago, since the language is now designed to deal with (and eliminate) large amount of identical definitions (templates, inline functions, etc.), but the One Definition Rule is still rooted in the principle of independent translation.

In addition to the above, in C++ language the point at which you define your variable will determine the order of its initialization with regard to other variables defined in the same translation unit. This is also a part of user intent, i.e. something the compiler cannot decide without your help.


Starting from C++17 you can declare your static members as inline. This eliminates the need for a separate definition. By declaring them in that fashion you effectively tell compiler that you don't care where this member is physically defined and, consequently, don't care about its initialization order.

Why we declare static variable in a class & the definition in outside of the class?

I'm not sure but my guess is, because inside a class member variables are only declared. They are initialized through a constructor or other member functions.

This happens when an object is instantiated. However for static members, the objects need not be instantiated. Hence they need to be initialized once outside of the class.

EDIT:

Actually it is not necessary to initialize the static variables, but it is necessary to define them outside the class to allocate memory for them. Only after their definition, can they be initialized and then used in the program.

Static data member initialization in the class definition?

why (for what?) it is impossible to initialize the static variable inside the class?

It is not impossible to initialise static variables inside the class definition, if they are const.

But actually without std::size_t const s::len - this line it's compiles successfully too, so in what cases it shouldn't work?

It works because the variable was not odr-used. See [basic.def.odr]:

A variable x whose name appears as a potentially-evaluated expression e is odr-used by e unless

  • x is a reference ...
  • x is a variable of non-reference type that is usable in constant expressions and has no mutable subobjects, and e is an element of the set of potential results of an expression of non-volatile-qualified non-class type to which the lvalue-to-rvalue conversion ([conv.lval]) is applied, or ...

Besides, ODR violations are no diagnostic required. This is technically true for missing definitions. An optimisation might remove odr-uses so that they don't manifest as errors.

Try the following to odr-use the variable:

const int *ptr = &X::n;
  1. Why const static data members may be defined in the class definition?

No. Non-inline const static data members are not defined by the declaration in the class definition. There needs to be a definition (outside the class) if the variable is odr-used.


  1. Why static data members [...] may not be defined in the class definition (see my thoughts about template classes with static members and Stroustrup words about this (does he cheat us?))?

Stroustrup is correct about "complicated linker rules". Those were avoided when it was not possible to define static variables in headers.

Your point about templates makes sense. Those complicated linker rules were needed anyway to make templated static variables possible. And being able to define even non-templated static variables has advantages. These are probably the reasons why the committee chose to allow defining static members in class definition in C++17. See: inline static member definition.



Related Topics



Leave a reply



Submit