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.