Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

const variables in header file and static initialization fiasco

After reading a lot of the questions regarding initialization of static variables I am still not sure how this applies to const variables at namespace level.

I have kind of the following code in a header file config.h generated by the build script:

static const std::string path1 = "/xyz/abc";
static const std::string path2 = "/etc";

According to what I have read the static keyword is not necessary, even deprecated here.

My Question: Is the code above prone to the static initialization fiasco?

If I have the following in a header file myclass.h:

class MyClass
{
public:
    MyClass(const std::string& str) : m_str(str) {}
    std::string Get() const { return m_str; }

private:
    std::string m_str;
}

const MyClass myclass1("test");

Will this pose any problems with static initialization?

If I understood right, due to const variables having internal linkage there should be no problem in both cases?

Edit: (due to dribeas answer)

Maybe I should mention that I am interested in use cases like:

In main.cpp:

#include <config.h>
#include <myclass.h>

std::string anotherString(path1 + myclass1.Get());

int main()
{
    ...
}

Another question regarding this use case: Will the compiler optimize away path2 in this case?

like image 741
Hanno S. Avatar asked Jan 26 '11 15:01

Hanno S.


People also ask

What is the static initialization order fiasco?

The static initialization order fiasco refers to the ambiguity in the order that objects with static storage duration in different translation units are initialized in.

Can we use const and static together?

So combining static and const, we can say that when a variable is initialized using static const, it will retain its value till the execution of the program and also, it will not accept any change in its value.

Can we initialize static variable in header file?

When a static value is used to initialize another static variable, the first may not be initialized, yet. // file. h class File { public: static struct _Extensions { const std::string h{ ". h" }; const std::string hpp{ ".

Do const variables need to be initialized?

A constant variable must be initialized at its declaration. To declare a constant variable in C++, the keyword const is written before the variable's data type. Constant variables can be declared for any data types, such as int , double , char , or string .


2 Answers

Your first definition places path1 in each compilation unit that includes config.h. To avoid this, don't define variables in header files. Usually you'd declare the variables in the header as extern:

extern const std::string path1;
extern const MyClass myclass1;

and define them in a translation unit, e.g. config.cpp:

const std::string path1 = "/xyz/abc";
const MyClass myclass1("test");

Sometimes you need a constant variable that is usable only from one translation unit. Then you can declare that variable at file scope as static.

static const std::string path1 = "/xyz/abc";

static is not deprecated any more. static and extern are sometimes implied, but I always forget where and how, so I usually specify them explicitly for all namespace-level variables.

like image 88
Philipp Avatar answered Sep 18 '22 15:09

Philipp


I tried to get the necessary information right from the C++03 Standard document. Here is what I found:

Regarding the const static declarations:

According to section 3.5.3 objects defined at namespace level and declared const have internal linkage by default. static also declares a namespace level object to have internal linkage so there is no need to declare an object static const.

Also according to Annex D.2

The use of the static keyword is deprecated when declaring objects in namespace scope (see 3.3.5).

Regarding the static initialization fiasco:

Since the variables are defined in a header file they are always defined before any other static objects using them.

From section 3.6.2.1:

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.

Answer 1: This means passing the variables to a static object constuctor should be fine.

Answer 2: However a problem could occur if the variables are referenced from a non-inline constructor of a static object:

Neither in section 3.6.2.1 nor 3.6.2.3 is it specified in which order the static objects in different compilation units are initialized if dynamic initialization is done before the first statement of main.

Consider the following:

// consts.h
#include <string>

const std::string string1 = "ham";
const std::string string2 = "cheese";

// myclass.h
#include <string>

class MyClass
{
public:
    MyClass();
    MyClass(std::string str);
    std::string Get() { return memberString; }
private:
    std::string memberString;
}

// myclass.cpp
#include "consts.h"
#include "myclass.h"

MyClass::MyClass() : memberString(string1) {}

MyClass::MyClass(std::string str) : memberString(str) {}

// main.cpp
#include <iostream>
#include "consts.h"
#include "myclass.h"

MyClass myObject1;
MyClass myObject2(string2);

using namespace std;

int main()
{
    cout << myObject1.Get(); // might not print "ham"
    cout << myObject2.Get(); // will always print "cheese"
}

Since myclass.cpp has its own copy of the const variables, these might not be initialized when MyClass::MyClass() is called.

So yes, const variables defined in header files can be used in a way that is prone to the static initialization fiasco

As far as I can see this does only apply to variables not requiring static initialization:

From C++03 standard, section 3.6.2.1:

Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.

like image 45
Hanno S. Avatar answered Sep 20 '22 15:09

Hanno S.