6

I am returning to C++ after a long absence and I am stumbling a little over my understanding of the fairly well known static initialization problem.

Let's say I have a simple class Vector2 as given below (note that I am aware that x and y should be private with getters and setters, these have just been omitted for brevity):

class Vector2 {

public:
  Vector2(float x, float y) :x(x), y(y) {};
  float x,y;
}

Now, if I want to specify a static const member to represent a Vector2 with x and y set to 1, I am unsure on how to proceed - will static const members fall foul of the static initialization problem or will the act of making them const mean they are ok? I am toying with the following possibilities:

Possibility 1:

// .h
class Vector2 {

public:
  Vector2(float x, float y) :x(x), y(y) {}
  static const Vector2 ONE;
  float x,y;
};

// .cpp
const Vector2 Vector2::ONE = Vector2(1.f, 1.f);

Possibility 2:

// .h
class Vector2 {

public:
  Vector2(float x, float y) :x(x), y(y) {}
  static const Vector2& getOne();
  float x,y;
private: 
  static const Vector2 ONE;
};

// .cpp
const Vector2 Vector2::ONE = Vector2(1.f, 1.f);

static const Vector2& Vector2::getOne() {
  return ONE;
}

Possibility 3:

// .h
class Vector2 {

public:
  Vector2(float x, float y) :x(x), y(y) {}
  static const Vector2& getOne();
  float x,y;
};

// .cpp
const Vector2& Vector2::getOne() {
  static Vector2 one(1.f,1.f);
  return one;
}

Now, my preferred way to write this would be as in possibility 2, just because it is a more comfortable syntax for me. However, if I call the getOne() method from another static method in another class am I going to risk crashing and burning? As I say, it is because I am using a static const rather than a plain static that I am asking this question as I have found much on plain static class member issues, but nothing on const static issues.

I suspect that I gain nothing by the fact that I am using static const and will need to go with Possibility 3 to be safe, but I just want to ask in case someone can shed some light on this for me.

I realise I am probably opening myself up to a slew of links pointing to exactly what I am asking, but I have looked and not found before posting this.

Any help will be gratefully appreciated.

Matt__E_
  • 277
  • 2
  • 9
  • It's not entirely clear from you question, but you are **only** prone to the fiasco thing if you try to access the static member from *another static initializer* directly or not. Accessing it from another function static or not, after main() has been called is OK – sbk Mar 03 '10 at 18:51
  • Thanks for your comment sbk. I should have been a bit clearer in my question. My intention was to ensure I was going to be safe for the static member to be used by a static initializer in another class in a separate cpp file. Although none of my other classes currently use Vector2::getOne() specifically to initialize another static member, I do have cases where there is a chain of static initializers. I chose Vector2 as a simple example so I could understand from the comments here the best pattern to roll out across my whole project. Many thanks for commenting. – Matt__E_ Mar 03 '10 at 23:20

2 Answers2

10

All of them, except possibility 3, suffer from the static initialization order fiasco. This is because your class is not a POD. In C++0x, this problem can be solved by marking the constructor constexpr, but in C++03 there is no such solution.

You can remove the constructor to solve the problem in C++03, and initialize using

const Vector2 Vector2::ONE = { 1.f, 1.f };

This is initializing a POD, and all initializers in the list are constant expression (for the purpose of static initialization). The intialization of them happen before any code is run that might access it before being initialized.

3.6.2:

Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization. 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.

8.5.1/14:

When an aggregate with static storage duration is initialized with a brace-enclosed initializer-list, if all the member initializer expressions are constant expressions, and the aggregate is a POD type, the initialization shall be done during the static phase of initialization (3.6.2); otherwise, it is unspecified whether the initialization of members with constant expressions takes place during the static phase or during the dynamic phase of initialization.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Thanks for your comment Johannes. I appreciate you taking the time to answer and for confirming of my 3 possibilities the 3rd is the only safe one to use. I hadn't thought of going down the POD initialization path so this is very informative. Although I have more complex classes where the POD approach won't work (as not all my classes can be treated as PODs) it is still very handy to know as I can definitely see places I can use this. Many thanks – Matt__E_ Mar 03 '10 at 23:48
  • I just realised that I had never accepted your answer. Sorry about that, but rectified now, nearly a year later – Matt__E_ Feb 25 '11 at 12:42
1

Please note that possibility 3 is not thread safe.

See for example "C++ scoped static initialization is not thread-safe, on purpose!" at http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx

Peter O.
  • 32,158
  • 14
  • 82
  • 96
dats
  • 236
  • 1
  • 10
  • 1
    Please note that this answer is no longer correct: thread safety is now [imposed by the standard](http://stackoverflow.com/questions/8102125/is-local-static-variable-initialization-thread-safe-in-c11) – Arnaud Apr 03 '15 at 20:51