Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using cout in the constructor of a class that is included in another class as a static member

Following code

#include <iostream>

struct A {
    A() {
        std::cout << std::endl;
    }
};

struct B {
    static inline A a;
};

int main() {
}

succeeds after compiling with gcc, but crashes with segmentation fault after compiling with clang. Is the code not standard or is clang wrong?

https://godbolt.org/z/tEvfrW

like image 296
tilin Avatar asked Jul 05 '20 07:07

tilin


People also ask

How do you access the static member of a class?

A static member function can be called even if no objects of the class exist and the static functions are accessed using only the class name and the scope resolution operator ::. A static member function can only access static data member, other static member functions and any other functions from outside the class.

What is static member of a class in C++?

Static data members are class members that are declared using static keywords. A static member has certain special characteristics. These are: Only one copy of that member is created for the entire class and is shared by all the objects of that class, no matter how many objects are created.

How do you initialize a static member variable of a class in C++?

For the static variables, we have to initialize them after defining the class. To initialize we have to use the class name then scope resolution operator (::), then the variable name. Now we can assign some value.

How can use static variable in C++ class?

static. The static keyword can be used to declare variables and functions at global scope, namespace scope, and class scope. Static variables can also be declared at local scope. Static duration means that the object or variable is allocated when the program starts and is deallocated when the program ends.

When is the constructor of a class called?

The constructor is called when an object of a class is created. It can be used to set initial values for object attributes: Note that the constructor name must match the class name, and it cannot have a return type (like void ). Also note that the constructor is called when the object is created.

Do all classes have constructors by default?

All classes have constructors by default: if you do not create a class constructor yourself, C# creates one for you. However, then you are not able to set initial values for fields. Constructors save time! Take a look at the last example on this page to really understand why.

Can a constructor have a return type?

Note that the constructor name must match the class name, and it cannot have a return type (like void ). Also note that the constructor is called when the object is created. All classes have constructors by default: if you do not create a class constructor yourself, Java creates one for you.

What is a static member in C++?

Learn about Static Member in C++. Member objects of a class are the class member variables that are objects of another class. If a class has an object of another class as a member variable that binds them in the Has-A relation. A class can have a static member, that can be declared by using the static keyword.


1 Answers

Cppreference on std::ios_base::Init reads:

The header <iostream> behaves as if it defines (directly or indirectly) an instance of std::ios_base::Init with static storage duration: this makes it safe to access the standard I/O streams in the constructors and destructors of static objects with ordered initialization (as long as #include <iostream> is included in the translation unit before these objects were defined).

You do include <iostream> before B::a, but the initialization of B::a (with B::a being static inline variable) is not part of ordered initialization, so it can be initialized before std::ios_base::Init. It seems that Clang (some versions, at least) does exactly this. This is a valid behaviour.

The standard reads ([basic.start.dynamic]):

  1. Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, is partially-ordered if the variable is an inline variable that is not an implicitly or explicitly instantiated specialization, and otherwise is ordered.

So, an initialization of instance of std::ios_base::Init is ordered, and initialization of B::a is partially-ordered.

  1. Dynamic initialization of non-local variables V and W with static storage duration are ordered as follows:

3.1. If V and W have ordered initialization and the definition of V is appearance-ordered before the definition of W, or if V has partially-ordered initialization, W does not have unordered initialization, and for every definition E of W there exists a definition D of V such that D is appearance-ordered before E, then ...

3.2. Otherwise, if the program starts a thread other than the main thread before either V or W is initialized, it is unspecified in which threads the initializations of V and W occur; the initializations are unsequenced if they occur in the same thread.

3.3. Otherwise, the initializations of V and W are indeterminately sequenced.

3.1 and 3.2 don't apply. So we have indeterminately sequenced initializations.

You can make B::a a non-inline static variable or somehow force std::ios_base::Init initialization before using std::cout, for example:

struct A {
    A() {
        std::cout << std::endl;
    }

    std::ios_base::Init init;
};
like image 88
Evg Avatar answered Oct 20 '22 06:10

Evg