Just wondering if in the following the static members are initialised before the Foo class object is initialised. Since both are static variables, one a static member and the other a static global variable, initialisation order is not guaranteed or specified.
struct Foo
{
Foo() { assert(a == 7); }
static inline int a = 7;
};
Foo foo;
int main()
{
}
So the initialisation order between the global Foo and the static class member is not defined, you would think there is no guarantee. However, I'm thinking that before a Foo is instantiated that the Foo class would need to be completed/initialised first, and so in that case that there might be a guarantee that the static member variable would be initialised first.
I'm thinking that before a Foo is instantiated that the Foo class would need to be completed/initialised first
That is not generally the case, so you should be careful with that assumption.
However, in your specific example, the order of initialization is guaranteed. The initializer of a is a constant expression and therefore a will be constant-initialized. Constant-initialization is guaranteed to happen before any dynamic initialization, which the initialization of foo is.
Even if a was not constant-initialized, there wouldn't be an issue here, because foo is defined after a in the same translation unit, foo is not an inline or template variable and because Foo is not a template. If either of these requirements were not fulfilled, there could be problems in the ordering guarantees.
Related
Why and when does the compiler optimize away static member variables? I have the following code
#include <iostream>
#include <typeinfo>
class X {
public:
X(const char* s) { std::cout << s << "\n"; };
};
template <class S> class Super {
protected:
Super() { (void)m; };
static inline X m { typeid(S).name() };
};
class A : Super<A> {
};
class B : Super<B> {
B() {};
};
class C {
static inline X m { "c" };
};
A a {};
int main() { return 0; }
On the output I can see that Super<A>::m, Super<B>:m, and C::m are all initialized.
Super<A>::m is not initialized if the statement A a {}; is removed. It makes sense because then m is never accessed. However this does not explain why it is not removed for B and C.
Is this behavior specified or is it an artifact of how the compiler detects unused variables?
The definition of a static data member of a class template specialization is implicitly instantiated only if it is used in such a way that a definition would be required.
For the class B you are, unconditionally, defining the default constructor. The default constructor uses the default constructor of Super<B> to initialize the base, meaning that the definition of the Super<B>::Super() constructor will be implicitly instantiated. This constructor's definition is odr-using m in (void)m; and therefore Super<B>::m's definition will also be implicitly instantiated.
In the case of class A, you are not explicitly defining any constructor. The implicit special member functions will be defined only when they are used in such a way that a definition would be required. In the line A a {}; you are calling the implicit default constructor of A and hence it will be defined. The definition will be calling the default constructor of Super<A> as before, requiring the Super<A>::m's definition to be instantiated. Without A a {}; there is nothing in the code requiring a definition of any special member function of A or the default constructor of Super<A> or the definition of m. Therefore none of them will be defined.
In the case of C, there is no template for which we would need to consider instantiation. C::m is explicitly defined.
Given that the static data member is defined, it must (generally) be initialized eventually. All of the inline static data members here have dynamic initialization with observable side effects, so the initialization must happen at runtime. It is implementation-defined whether they will be initialized before main's body starts execution or whether initialization will be deferred upto the first non-initialization odr-use of the inline static data member. (This is meant to allow for dynamic libraries.)
You aren't actually non-initialization odr-using any of the inline static data members, so it is implementation-defined whether they will actually be initialized at all. If the implementation does define the initialization to not be deferred, then all of these inline static data members which have been defined will also be initialized before main is entered.
The order in which the initializations will happen is indeterminate. The static data members of the class template specializations have unordered initialization, meaning they have no ordering guarantees with any other dynamic initialization. And there is only one static data member which isn't specialized from a template and that one is inline and therefore only partially ordered, although there is nothing else it could be ordered with.
Actually, there is one additional static storage duration object which will be initialized here, a global variable of type std::ios_base::Init included through <iostream>. The initialization of this variable causes the initialization of the standard streams (std::cout, etc.). Because your inline static data members from the templates have unordered initialization, they will not be ordered with this initialization. Similarly if you had multiple translation units containing C::m, it would also not be ordered with it. As a consequence you might be using std::cout before it is initialized, causing undefined behavior. You can cause early initialization of the standard streams by constructing an object of type std::ios_base::Init:
class X {
public:
X(const char* s) {
[[maybe_unused]] std::ios_base::Init ios_base_init;
std::cout << s << "\n";
};
};
Aside from considerations such as above, the compiler is not allowed to remove static data members if their initialization has observable side effects. Of course the as-if rule still applies as always meaning that the compiler can compile to whatever machine instructions which will result in the same observable behavior as described above.
For practical purposes you should also be careful. There are some compiler flags that are sometimes used for code size optimization which will eliminate dynamic initialization if the variable seems to be unused. (Although that is not standard-conforming behavior.) For example the --gc-sections linker flag together with GCC's -ffunction-section -fdata-section can have this effect.
As you can see dynamic initialization of static storage duration objects is kind of complicated in C++. In your case here there are only minor dependency issues, but this can quickly become very messy, which is why it is usually recommended to avoid it as much as possible.
I'm new to C++, and I just can't understand that why I can't assign a value to a non-const static member inside a class (like we do in java static int x = 12; ) even thought I can
declare a non-const static member (static int x;)
declare a static const member (static const x;)
assign a static const member (static const int x = 12;)
note: my class and my main() function are in the same file
In general
A static variable inside a class, just like everything else, is just a declaration by default. C++ then requires you to provide a single definition per entity that requires one, that's the One Definition Rule. The definition is where the initializer (which is not an assignment, but a construction) goes, since there should be only one as well. It is typically placed in a .cpp file so that it can't be accidentally duplicated by #includes.
The constant case
When a static member is a constant integer or enumeration, and is initialized with a compile-time expression, the initializer is allowed to be placed on the declaration, and the definition skipped. This is a result of a combination of old rules and isn't really interesting today IMO.
Proper inline initialization
Since C++17 introduced inline variables, you can use them as static members. The rules are roughly the same as inline functions, and are pretty sensible: you can provide multiple definition of an inline entity, you are responsible for ensuring that they are all strictly identical, and the implementation is responsible for collapsing them into a single definition with its initializer. Thus, what you're looking for is written:
struct Foo {
static inline int bar = 42;
// ^^^^^^
};
You have to initialise the static member outside the class definition like in this example:
class Box {
public:
static int x;
};
// Initialize static member of class Box outside the class definition
int Box::x = 12;
int main(void) {
...
}
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?
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.
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.
"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.
What is the reasoning to why static const members cannot exist in local classes? It seems like a rather silly restriction.
Example:
void foo() {
struct bar {
int baz() { return 0; } // allowed
static const int qux = 0; // not allowed?!?
};
}
struct non_local_bar {
int baz() { return 0; } // allowed
static const int qux = 0; // allowed
};
Quote from standard (9.8.4):
A local class shall not have static data members.
From the standard section 9.4.2:
If a static data member is of const integral or const enumeration
type, its declaration in the class definition can specify a
constant-initializer which shall be an integral constant expression.
In that case, the member can appear in integral constant expressions
within its scope. The member shall still be defined in a namespace
scope if it is used in the program and the namespace scope definition
shall not contain an initializer.
Basically, local classes have no linkage, and static data members require a linkage.
Since there's no way to define a static data member of a local class in namespace scope (a declaration with an initializer is not a definition), they are not allowed, whether they are of const integral type or not. On the surface it may seem like the compiler should just be able to inline the value, but then what happens if you try to access a pointer to the member? With namespace scoped classes you'd just get a linker error, but local classes have no linkage.
I guess in theory they could just allow you to use static const integral types in local classes as long as they are only used in integral constant expressions, but it would probably just put too much of a burden on the standards body and compiler vendors to differentiate for very little practical value; local static variables are accessible from local classes, so using a local static const should be just as good.
I dont think there is a.reason. Normal static datamembers are disallowed because there is no way to define them after being declared.
Also dont forget you can create a local const variable outside the.class that you can use inside the class as long as you only read its value (that is, as long as you dont take.its.address).
Static members of a class need to be defined in global scope, e.g.
abc.h
class myClass {
static int number;
};
abc.cpp
int myClass::number = 314;
Now, since the scope inside void abc(int x) is not global, there is no scope to define the static member.
As things progress, we now have C++11 and with that you can define integral constants variable members in your classes.
class test
{
public:
const int FOO = 123;
[...snip...]
};
That works when you compile with C++11. Notice that the static keyword is not used. When compiling with optimizations turned on, those variables will likely all get optimized out. In debug, though, they appear in your structure as regular variable members.
Note, however, that the size of the class/structure will still include that variable. So here it is likely 4 bytes for the variable FOO.
However, in most cases, classes defined in a function will completely be optimized out so this is a great way of doing things (a good 50% of my classes have such variable members!)
What is the reasoning to why static const members cannot exist in local classes? It seems like a rather silly restriction.
Example:
void foo() {
struct bar {
int baz() { return 0; } // allowed
static const int qux = 0; // not allowed?!?
};
}
struct non_local_bar {
int baz() { return 0; } // allowed
static const int qux = 0; // allowed
};
Quote from standard (9.8.4):
A local class shall not have static data members.
From the standard section 9.4.2:
If a static data member is of const integral or const enumeration
type, its declaration in the class definition can specify a
constant-initializer which shall be an integral constant expression.
In that case, the member can appear in integral constant expressions
within its scope. The member shall still be defined in a namespace
scope if it is used in the program and the namespace scope definition
shall not contain an initializer.
Basically, local classes have no linkage, and static data members require a linkage.
Since there's no way to define a static data member of a local class in namespace scope (a declaration with an initializer is not a definition), they are not allowed, whether they are of const integral type or not. On the surface it may seem like the compiler should just be able to inline the value, but then what happens if you try to access a pointer to the member? With namespace scoped classes you'd just get a linker error, but local classes have no linkage.
I guess in theory they could just allow you to use static const integral types in local classes as long as they are only used in integral constant expressions, but it would probably just put too much of a burden on the standards body and compiler vendors to differentiate for very little practical value; local static variables are accessible from local classes, so using a local static const should be just as good.
I dont think there is a.reason. Normal static datamembers are disallowed because there is no way to define them after being declared.
Also dont forget you can create a local const variable outside the.class that you can use inside the class as long as you only read its value (that is, as long as you dont take.its.address).
Static members of a class need to be defined in global scope, e.g.
abc.h
class myClass {
static int number;
};
abc.cpp
int myClass::number = 314;
Now, since the scope inside void abc(int x) is not global, there is no scope to define the static member.
As things progress, we now have C++11 and with that you can define integral constants variable members in your classes.
class test
{
public:
const int FOO = 123;
[...snip...]
};
That works when you compile with C++11. Notice that the static keyword is not used. When compiling with optimizations turned on, those variables will likely all get optimized out. In debug, though, they appear in your structure as regular variable members.
Note, however, that the size of the class/structure will still include that variable. So here it is likely 4 bytes for the variable FOO.
However, in most cases, classes defined in a function will completely be optimized out so this is a great way of doing things (a good 50% of my classes have such variable members!)