C++ static initialization of stateless class
Asked Answered
S

3

7

Suppose I have a class T where

  1. T has no virtual functions.
  2. T instances have no state.
  3. T has static member instances of itself.
  4. T itself has no other state.

Can the C++ static initialization fiasco ruin my program? I don't think so because even if one of the static instances is not initialized before use, that should not matter because T objects are stateless.

I'm interested in doing this for enum-like classes like so:


// Switch.h

class Switch {
public:
    static Switch const ON;
    static Switch const OFF;
    bool operator== (Switch const &s) const;
    bool operator!= (Switch const &s) const;
private:
    Switch () {}
    Switch (Switch const &); // no implementation
    Switch & operator= (Switch const &); // no implementation
};

// Switch.cpp

Switch const Switch::ON;
Switch const Switch::OFF;

bool Switch::operator== (Switch const &s) const {
    return this == &s;
}

bool Switch::operator!= (Switch const &s) const {
    return this != &s;
}
Scripture answered 16/5, 2011 at 22:2 Comment(5)
Excellent question & an interesting idea!Gyn
what does this give you over a simple enum (without the extra state)?Cripps
Please post an example of how you actually intend to use the Switch class.Electra
The class declaration is in a header and the operator and member definitions are in some cpp file, right? Otherwise, this question is about the one-definition rule, not the static-initialization-order fiasco. Please indicate whether your code example is two separate files.Guiscard
Whoops, sorry. This is indeed supposed to be in two separate files.Scripture
R
2

To answer the first part of your question, if T has a constructor which has side effects then you can in fact get burned by static initialization fiasco.

Ribosome answered 16/5, 2011 at 22:34 Comment(0)
M
2

I am interested in what are the advantages that you see from, say, an enum wrapped in either a namespace or a class:

namespace Switch {
   enum Switch {
      ON,
      OFF
   };
}

It will be simpler to use in most cases (in your implementation you require users to employ either references or pointers, as the objects are non-copyable), it requires less code (no need to disable the constructors, and create the operators)...

As a matter of fact, in the upcoming standard you almost get that for free without even the use of the namespace:

enum Switch {
   ON,
   OFF
};
// bad, it allows this (as in the current standard):
Switch s = ON;
// good, it does also allow explicit qualification:
Switch s = Switch::ON;
Mcculley answered 16/5, 2011 at 22:19 Comment(1)
I never considered this. Though to be a devil's advocate, the class variant I proposed does not allow implicit conversion to ints. Also you can't reopen a class, but that can be solved by wrapping in a class instead of a namespace I guess.Scripture
R
2

To answer the first part of your question, if T has a constructor which has side effects then you can in fact get burned by static initialization fiasco.

Ribosome answered 16/5, 2011 at 22:34 Comment(0)
L
0

Do you really intend to use pointer values to compare "state"? I agree with @Drew, it's an interesting idea. I'm not sure it is guaranteed by the standard to work, though, if we assume that this is a header-only implementation.

Consider what happens when multiple compilation objects contain the same definition for Switch::ON and Switch::OFF. Since these are variables, and not functions, the linker would have to decide, arbitrarily, between them.

When you ran a test, what did the popular compilers say: gcc 3, gcc 4, microsoft C++ 2005, 2008, and 2010, and one of the Edison Design Groups' compilers such as http://www.comeaucomputing.com/ ?

Said test would consist of:

// Switch.h
class Switch {
public:
    static Switch const ON;
    static Switch const OFF;
    bool operator== (Switch const &s) const;
    bool operator!= (Switch const &s) const;
private:
    Switch () {}
    Switch (Switch const &); // no implementation
    Switch & operator= (Switch const &); // no implementation
};

Switch const Switch::ON;
Switch const Switch::OFF;

bool Switch::operator== (Switch const &s) const {
    return this == &s;
}

bool Switch::operator!= (Switch const &s) const {
    return this != &s;
}

and

// main.cpp
#include "Switch.h"

extern int another_test();

int main(int argc, char*argv[])
{
  another_test();
  const Switch& current_state = Switch::ON;
  const Switch& another_state = Switch::OFF;
  if (current_state == another_state) {
    return 1;
  } else if (current_state != another_state) {
    return 2;
  }
  return another_test();
}

and

// another_test.cpp
#include "Switch.h"

int another_test()
{
  const Switch& current_state = Switch::ON;
  const Switch& another_state = Switch::OFF;
  if (current_state == another_state) {
    return 4;
  } else if (current_state != another_state) {
    return 5;
  }
  return 6;
}
Legist answered 16/5, 2011 at 22:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.