Swapnil
Swapnil

Reputation: 1568

Why can Static data member not be initialized in-class in c++11?

With the new feature of c++11, we are able to do In-class member initialisation. But still static data member cannot be defined in class.

class A
{
    static const int i = 10;
    int j = 10;
    const int k = 20;
    static int m = 10; // error: non-const static data member must be initialized out of line
};

Why is this feature not provided?

Upvotes: 3

Views: 17871

Answers (3)

AnT stands with Russia
AnT stands with Russia

Reputation: 320797

"This feature is not provided" because in-class initialization for non-static and static members are semantically very different features. Your suprize is based on the fact that they look superficially similar. But in reality they actually have nothing in common.

In-class declaration of a static data member is just that - a declaration. It does not provide definition for that member. The definition has to be provided separately. The placement of that definition in the code of your program will have consequences. E.g. it will also define the initialization behavior for that static data member (the order of initialization) and it will affect the exported symbols in object files. This is why choosing the location for that definition is your responsibility. The language wants you to do it and it wants you to do it explicitly. These issues do not apply to non-static members, which is what makes them fundamentally different.

If you don't care about such matters, starting from C++17 you can explicitly tell the compiler that you don't care by declaring your static member inline. Once you do that, you'll be able to initialize it in-class.

Upvotes: 4

bolov
bolov

Reputation: 75924

Non-static data member in-class initialization

First of, this is completely different than static member initialization.

In-class member initialization is just a syntactic sugar that transforms to constructor initialization list.

E.g.

struct X
{
   int a_ = 24;
   int b_ = 11;
   int c_;

   X(int c) : c_{c}
   {
   }

   X(int b, int c) : b_{b}, c_{c}
   {
   }
};

Pretty much is equivalent to:

struct X
{
   int a_;
   int b_;
   int c_;

   X(int c) : a_{24}, b{11}, c_{c}
   {
   }

   X(int b, int c) : a{24}, b_{b}, c_{c}
   {
   }
};

Just syntactic sugar. Nothing that couldn't been done previous to C++11 with more verbose code.

Static data member in-class initialization

Things are more complicated here because there has to be just 1 symbol for the static data member. You should read about ODR (One Definition Rule).

Let's start with const static data member. You might be surprised that initialization is allowed only from compile time constant expressions:

auto foo() { return 24; }
constexpr auto bar() { return 24 };

struct X
{
    static const int a = foo(); // Error
    static const int b = bar(); // Ok
};

The actual rule (well not a rule per se, but a reasoning if you will) is more general (for both const and non-const static data members): static data member initialization, if in line, must be a compile time expression. This effectively means that the only static data member in line initialization allowed is for const static data members with constexpr initialization.

Now let's see the reasoning behind that: if you have an in line initialization that would make it a definition and this means that every compilation unit where the definition of X appears will have a X::a symbol. And every such compilation unit will need to initialize the static member. In our example foo would be called for each compilation unit that includes directly or indirectly the header with the definition of X.

The first problem with this is that it's unexpected. The number of calls to foo will depend on the number of compilation units that have included X, even if you wrote a single call to foo for a single initialization of a single static member.

There is a more serious problem though: foo not being a constexpr function nothing prevents foo from returning different results on each invocation. So you will end up with a bunch of X::a symbols which should be under ODR but each of them initialized with different values.

If you are still not convinces then there is the 3rd problem: having multiple definition of X::a would simply be a violation of ODR. So... the previous two problems are just some of the motivations for why ODR exists.

Forcing an out-of line definition for X::a is the only way which allows correct definition and initialization of X::a: in a single compilation unit. You can still mess up and write the out of line definition and initialization in a header, but with an in line initialization you definitely have multiple initializations.

As n.m. showed since C++17 you have inline data members and here we are allowed in-class initialization:

struct X
{
    static inline int i = foo();
};

Now we can understand why: with inline the compiler will chose just one definition of X::i (from one compilation unit) and so you just have one evaluation of the initialization expression that is chosen from one compilation unit. Note that it is still your duty to respect ODR.

Upvotes: 14

n. m. could be an AI
n. m. could be an AI

Reputation: 120239

It is provided in C++17.

 static inline int m = 10; 

Q. Why wasn't it provided in C++11?

A. Because it wasn't ready back then. (You can ask the same question about every single new language feature, and the answer is always the same too.)

Q. Why does it require the inline keyword?

A. For simplicity of compiler development, better expressivity, and/or better consistency with the other parts of the language. Most likely there's some weighed combimation of several factors.

Upvotes: 4

Related Questions