Why is an inline static variable of a template class not initialized? - c++

Minimal example and question
In this example, an inline static member variable c2 of a nontemplate class is initialized when the class member is instantiated, and member variable c1 of a template class is not. What is the difference? Why is c1 not being initialized unless I force it to be by taking its address, and c2 is initialized unconditionally?
struct C1 {
C1() { std::cerr << "C1()\n"; }
};
struct C2 {
C2() { std::cerr << "C2()\n"; }
};
template<typename T>
struct Template {
inline static C1 c1;
};
struct Nontemplate {
inline static C2 c2;
};
int main() {
Template<int> a;
Nontemplate b;
(void)a;
(void)b;
}
// Output:
C2()
Context and reasoning
Here's a bit of context to the minimal example. I have the Nontemplate class inherited from Template<something>, and the constructor of c2 depends on c1. I expect c1 to be created before c2; however, that is not the case.
template<typename T>
struct Template {
inline static C1 c1;
};
struct Nontemplate : public Template<int> {
struct C2 {
C2() {
std::cerr << "Do something with Nontemplate::C1\n";
std::cerr << "&Nontemplate::c1 = " << &Nontemplate::c1 << "\n";
}
};
inline static C2 c2;
};
int main() {
Nontemplate b;
(void)b;
}
// Output:
Do something with Nontemplate::C1
&Nontemplate::c1 = 0x600ea8
C1()
The code was compiled with g++ 7.2 with -std=c++17 flags. Both -O0 and -O2 give the same result.

Implicit instantiation of a class template only causes instantiation of the declarations it contains. Definitions are generally only instantiated when they are used in a context that requires the definition to exist.
So as long as you don't use Template<int>::c1 in a way that would require its definition to exist (i.e. by odr-using it), then it will not be defined at all.
One way of odr-using the variable is to take its address, as you mentioned.
Even if you force the instantiation of the variable, there is no guarantee when exactly it will be initialized.
C1's constructor is not constexpr and so the initialization of Nontemlate::c1 cannot be a constant expression. This means that you are going to get dynamic initialization of Template<int>::c1. Dynamic initialization of global static variables which are part of a template specialization are unordered, meaning that there is no guarantee in what order they will happen relative to any other dynamic initialization of global static variables.
Similarly Nontemlate::c2 is not initialized by a constant expression and so is also dynamically initialized. Although Nontemlate::c2 has partially ordered dynamic initialization (being an inline variable not part of a template specialization), it is still indeterminately sequenced relative to Template<int>::c1 as explained above.
It is also not strictly required that Template<int>::c1 and Nontemlate::c2 are initialized before main is entered. It is implementation-defined whether initialization is deferred until later, but before the first odr-use of the respective variable. I think this deferral is used mainly for runtime dynamic library loading, though.
A common method to avoid ordering issues of global static storage duration variables is to use a function returning a reference to a local static variable instead, i.e.:
template<typename T>
struct Template {
static auto& c1() {
static C1 instance;
return instance;
}
};
although this may have a performance impact when called often, because the construction of the local static must be checked each time.
Alternative, if the initializer can be made a constant expression, making the variable constexpr should guarantee constant initialization, meaning that no dynamic initialization will happen at all and no ordering issues will be present.

Related

Definition of static data member without repeating its type

When I have a class with a static const or constexpr data member, defining that variable reqires me to repeat stuff:
/// my_class.hpp
class my_class { constexpr static int x = 1; };
/// my_class.cpp
#include "my_class.hpp"
// auto my_class::x; // error: declaration of 'auto my_class::x' has no initializer
// decltype(auto) my_class::x; // error: declaration of 'decltype(auto) my_class::x' has no initializer
decltype(my_class::x) my_class::x; // ok
Of course I could do
#define DEFINE_STATIC_DATA_MEMBER(x) decltype(x) x
DEFINE_STATIC_DATA_MEMBER(my_class::x);
but I wonder if there’s a non-macro solution.
The question arose because both the type and the fully-qualified name of the static data member are lengthy and I’m likely to get more of these.
Starting from C++17 you don't need to separately define static constexpr variables.
Just class my_class { constexpr static int x = 1; }; is enough, without a .cpp file.
You can make use of a typedef to not have to repeat the type when defining it.
my_class.hpp
class my_class
{
// Declaration
static const my_very_lengthy_type_name_I_dont_want_to_repeat x;
// typedef
using t = decltype(x);
};
my_class.cpp
#include "my_class.hpp"
// Initialization
my_class::t my_class::x = {};
defining that variable requires me to repeat stuff:
For static constexpr data member, the above statement is true only for Pre-C++17 standard.
From C++17 onwards, we're allowed to omit writing an out-of-class definition of a static constexpr data member. This is illustrated in the example given below.
C++11
class Foo
{
public:
static constexpr int OUT_OF_BOUNDS_VALUE = -9999; //THIS IS A DECLARATION IN C++11 and C++14
//other members here
};
In the above code snippet(which is for C++11,C++14), we have a declaration of the static data member OUT_OF_BOUNDS_VALUE inside the class. And so, in exactly one translation unit we have to provide a corresponding definition. Otherwise you'll get a linker error which can be seen here.
That is, in exactly one translation unit we should write:
constexpr int Foo::OUT_OF_BOUNDS_VALUE; //note no initializer
After adding the above out of class definition with no initializer the program will work. Demo
C++17
But the out-of-class definition for static constexpr is no longer needed from C++17 onwards.
class Foo
{
public:
static constexpr int OUT_OF_BOUNDS_VALUE = -9999; //THIS IS A DEFINITION IN C++17
//other members here
};
In the above code snippet(which is for C++17) we have a definition of the static data member OUT_OF_BOUNDS_VALUE inside the class. So since C++17, we don't have to provide the definition of OUT_OF_BOUNDS_VALUE anywhere else since we already have a definition for it inside the class and thus the same program works without any linker error.

Why does the definition of constructor make different value

I'm testing the piece of code below:
#include <iostream>
struct foo
{
foo() {}
int a;
};
struct bar
{
bar();
int b;
};
bar::bar() = default;
int main()
{
foo f{};
bar b{};
std::cout << f.a << '\t' << b.b << std::endl;
}
The output is 0 21946.
Well, it seems that the object f is initialized with Zero initialization but the object b is initialized with default initialization. The default initialization of an int is a random number and that's why I got a 21946.
Why did I get two different kinds of initialization here?
I knew that the variable with static storage duration may be initialized with Zero initialization and it put at the .bss segment, this is a kind of static initialization. But foo f{} is obviously a kind of dynamic initialization. Why is f initialized with Zero initialization, instead of default initialization? Why does the compiler make two different behavior?
From cppreference:
Defaulted default constructor outside of class definition (the class must contain a declaration (1)). Such constructor is treated as user-provided
Therefore, none of the statements are value-initializations.
Both are default initialization because they have user-provided constructors. And the values of non-static data members leaves uninitialized (can contain an arbitrary number, including zero)
To do the value initialization for the defaulted constructor case, use the in-class declaration:
struct bar
{
bar() = default;
int b;
};

Template, Inheritance, static member incrementation

the following code in my opinion should output sum sizeof(int) + sizeof(float) + sizeof(std::string), but the storage value is always zero. Why?
struct Base {
static int IncrementID(int x) {
static int id = 0;
storage += x;
return id++;
}
static int storage;
};
int Base::storage = 0;
template<typename T>
struct Object : public Base {
static const int id;
};
template<typename T>
const int Object<T>::id(Base::IncrementID(sizeof(T)));
int main() {
Object<int> a;
Object<float> b;
Object<std::string> c;
std::cout << Base::storage;
}
You don't use those static id data members in any way that can cause their implicit instantiation. It means they don't have to be instantiated (nor their initialization has to happen). To quote the C++ standard:
[temp.inst]
3 Unless a member of a class template or a member template has
been explicitly instantiated or explicitly specialized, the
specialization of the member is implicitly instantiated when the
specialization is referenced in a context that requires the member
definition to exist; in particular, the initialization (and any
associated side effects) of a static data member does not occur unless
the static data member is itself used in a way that requires the
definition of the static data member to exist.
Doing something as simple as adding a user defined constructor like
Object() {
int i = id;
(void)i;
}
Can be enough to odr-use them, and as such force their instantiation by creating objects as you do.

Initialization of a member when passing parameters to a base class constructor

In the code below i'm assigning a value to uninitialized class member upon creation of a base class. Why didn't this cause runtime error?
class Foo {
public:
Foo(std::string) {}
};
class Bar : public Foo {
public:
Bar() : Foo(s = f()) {} // assigning to uninitialized class member
void print() {
std::cout << s << std::endl;
}
std::string f() {
return "Some string";
}
private:
std::string s;
};
int main()
{
Bar b;
b.print();
return 0;
}
But adding another one member to the class Bar produces an error on creation of the Foo class:
Class Bar {
// same as above
private:
std::string s;
int a; // adding another member
};
Why do this happens?
"Runtime error"? There's no "runtime error" when assigning to an uninitialized member. In C++ the state of "being uninitialized" is not detectable at run-time, which is why it cannot possibly be handled in any deterministic manner (like with "runtime error").
When you do something like that, your code exhibits undefined behavior. The away this undefined behavior will manifest itself is unpredictable. It can easily be sensitive to completely unrelated factors, like declaring another member in the class. That's all there is to it.
When f() executes the class Bar is uninitialized so the member 'a' has no value. It's interesting that this code runs in visual studio 2008 but in any case this is not safe to do.
Razvan.
I tried to debug your code. It seems that, when you write:
Bar() : Foo(s = f()) {}
The s object is not properly created yet. Because constructor call of Foo() is not complete yet. Basically it is in a undefined state. So any behaviour will be undefined.
But this code behaviour is not undefined:
Bar() : Foo(/*anything*/) {
s = f();
}
Because the constructor call is complete.
Note: I tested in msvc11
Base class and class members are initialized in order of declaration (virtual base classes are different):
Base classes (first) in order of declaration (Eg. class X : A, B, C }
Members in order of declaration (Eg. int a; int b; int c;)
The order at initialization in a constructor does not change it.
In your case Foo is uninitialized, hence Bar and it's members - undefined behaviour (s has some rubbish data while assigning to it).

In this specific case, is there a difference between using a member initializer list and assigning values in a constructor?

Internally and about the generated code, is there a really difference between :
MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}
and
MyClass::MyClass()
{
_capacity=15;
_data=NULL;
_len=0
}
thanks...
You need to use initialization list to initialize constant members,references and base class
When you need to initialize constant member, references and pass parameters to base class constructors, as mentioned in comments, you need to use initialization list.
struct aa
{
int i;
const int ci; // constant member
aa() : i(0) {} // will fail, constant member not initialized
};
struct aa
{
int i;
const int ci;
aa() : i(0) { ci = 3;} // will fail, ci is constant
};
struct aa
{
int i;
const int ci;
aa() : i(0), ci(3) {} // works
};
Example (non exhaustive) class/struct contains reference:
struct bb {};
struct aa
{
bb& rb;
aa(bb& b ) : rb(b) {}
};
// usage:
bb b;
aa a(b);
And example of initializing base class that requires a parameter (e.g. no default constructor):
struct bb {};
struct dd
{
char c;
dd(char x) : c(x) {}
};
struct aa : dd
{
bb& rb;
aa(bb& b ) : dd('a'), rb(b) {}
};
Assuming that those values are primitive types, then no, there's no difference. Initialization lists only make a difference when you have objects as members, since instead of using default initialization followed by assignment, the initialization list lets you initialize the object to its final value. This can actually be noticeably faster.
Yes. In the first case you can declare _capacity, _data and _len as constants:
class MyClass
{
private:
const int _capacity;
const void *_data;
const int _len;
// ...
};
This would be important if you want to ensure const-ness of these instance variables while computing their values at runtime, for example:
MyClass::MyClass() :
_capacity(someMethod()),
_data(someOtherMethod()),
_len(yetAnotherMethod())
{
}
const instances must be initialized in the initializer list or the underlying types must provide public parameterless constructors (which primitive types do).
I think this link http://www.cplusplus.com/forum/articles/17820/ gives an excellent explanation - especially for those new to C++.
The reason why intialiser lists are more efficient is that within the constructor body, only assignments take place, not initialisation. So if you are dealing with a non-built-in type, the default constructor for that object has already been called before the body of the constructor has been entered. Inside the constructor body, you are assigning a value to that object.
In effect, this is a call to the default constructor followed by a call to the copy-assignment operator. The initialiser list allows you to call the copy constructor directly, and this can sometimes be significantly faster (recall that the initialiser list is before the body of the constructor)
I'll add that if you have members of class type with no default constructor available, initialization is the only way to construct your class.
A big difference is that the assignment can initialize members of a parent class; the initializer only works on members declared at the current class scope.
Depends on the types involved. The difference is similar between
std::string a;
a = "hai";
and
std::string a("hai");
where the second form is initialization list- that is, it makes a difference if the type requires constructor arguments or is more efficient with constructor arguments.
The real difference boils down to how the gcc compiler generate machine code and lay down the memory. Explain:
(phase1) Before the init body (including the init list): the compiler allocate required memory for the class. The class is alive already!
(phase2) In the init body: since the memory is allocated, every assignment now indicates an operation on the already exiting/'initialized' variable.
There are certainly other ways to handle const type members. But to ease their life, the gcc compiler writers decide to set up some rules
const type members must be initialized before the init body.
After phase1, any write operation only valid for non-constant members.
There is only one way to initialize base class instances and non-static member variables and that is using the initializer list.
If you don't specify a base or non-static member variable in your constructor's initializer list then that member or base will either be default-initialized (if the member/base is a non-POD class type or array of non-POD class types) or left uninitialized otherwise.
Once the constructor body is entered, all bases or members will have been initialized or left uninitialized (i.e. they will have an indeterminate value). There is no opportunity in the constructor body to influence how they should be initialized.
You may be able to assign new values to members in the constructor body but it is not possible to assign to const members or members of class type which have been made non-assignable and it is not possible to rebind references.
For built in types and some user-defined types, assigning in the constructor body may have exactly the same effect as initializing with the same value in the initializer list.
If you fail to name a member or base in an initializer list and that entity is a reference, has class type with no accessible user-declared default constructor, is const qualified and has POD type or is a POD class type or array of POD class type containing a const qualified member (directly or indirectly) then the program is ill-formed.
If you write an initializer list, you do all in one step; if you don't write an initilizer list, you'll do 2 steps: one for declaration and one for asign the value.
There is a difference between initialization list and initialization statement in a constructor.
Let's consider below code:
#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>
class MyBase {
public:
MyBase() {
std::cout << __FUNCTION__ << std::endl;
}
};
class MyClass : public MyBase {
public:
MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
std::cout << __FUNCTION__ << std::endl;
}
private:
int _capacity;
int* _data;
int _len;
};
class MyClass2 : public MyBase {
public:
MyClass2::MyClass2() {
std::cout << __FUNCTION__ << std::endl;
_capacity = 15;
_data = NULL;
_len = 0;
}
private:
int _capacity;
int* _data;
int _len;
};
int main() {
MyClass c;
MyClass2 d;
return 0;
}
When MyClass is used, all the members will be initialized before the first statement in a constructor executed.
But, when MyClass2 is used, all the members are not initialized when the first statement in a constructor executed.
In later case, there may be regression problem when someone added some code in a constructor before a certain member is initialized.
Here is a point that I did not see others refer to it:
class temp{
public:
temp(int var);
};
The temp class does not have a default ctor. When we use it in another class as follow:
class mainClass{
public:
mainClass(){}
private:
int a;
temp obj;
};
the code will not compile, cause the compiler does not know how to initialize obj, cause it has just an explicit ctor which receives an int value, so we have to change the ctor as follow:
mainClass(int sth):obj(sth){}
So, it is not just about const and references!