0

I face the following problem :

I have a Color class, that for the purpose of this question, reduces to :

// Color.h
struct Color {
    int r,g,b;
    Color() : r(0), g(0), b(0) {}
    Color(int r_, int g_, int b_) : r(r_), g(g_), b(b_) {}
    static const Color Red;
    static const Color Magenta;
};

// Color.cpp
#include "Color.h"
const Color Color::Red (255,0,0);
const Color Color::Magenta (255,0,255);

I.e, I want to use the class name as a scope for some predefined colors.

Now in a user file in a global scope I do :

//user.cpp
#include "Color.h"
static const Color colors[2] = { Color::Red, Color::Magenta };

And when I use colors[i] I see that they were filled with 0s. I looked it up and saw that the empty constructor is first called (that doesn't make sense for a reason soon to reveal), and then I changed the empty c'tor to :

    Color() : r(200), g(200), b(200) {}

And got the same result.

I tried to define the colors as constexpr like this :

    static constexpr Color Red (255, 0, 0);

But it said : expected identifier before numeric constant

And then like this :

    static constexpr Color Red = {255, 0, 0};

And like this :

    static constexpr Color Red = Color(255, 0, 0);

But then the compilation failed because "'Color' was not declared in this scope", and "Red has incomplete type" (really?) So now it really doesn't make sense that the empty c'tor was used, but rather this whole memory was initialized with 0.

In runtime I can use the static const Colors and it works.

I this behaviour even well-defined? Does it depend on compilation / linkage order?

How do I work around it?

Thanks

2
  • 3
    Order of initialization of global variables defined in different translation units is unspecified. In your case, colors gets initialized before Red and Magenta. I think, but am not sure, that marking the constructor itself constexpr might help, as in constexpr Color(int r_, int g_, int b_) : r(r_), g(g_), b(b_) {} Commented May 28, 2017 at 15:35
  • sounded promising, but it didn't.. why is the class incomplete type within itself? is it the only way to prevent endless recursion of a member? Commented May 28, 2017 at 18:03

1 Answer 1

3

You are running into the Static Initialization Order Fiasco. It sounds like your colors array is getting initialized before the Red and Magenta objects are.

One solution is the Construct On First Use Idiom:

// Color.h
struct Color {
    int r,g,b;
    Color() : r(0), g(0), b(0) {}
    Color(int r_, int g_, int b_) : r(r_), g(g_), b(b_) {}
    static const Color& Red();
    static const Color& Magenta();
};

// Color.cpp
#include "Color.h"
const Color& Color::Red()
{ static const Color red(255,0,0); return red; }
const Color& Color::Magenta()
{ static const Color magenta(255,0,255); return magenta; }

//user.cpp
#include "Color.h"
static const Color colors[2] = { Color::Red(), Color::Magenta() };
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, but I prefer not using singleton colors, especially as these colors are widely used already, and I needed the static array just this once.. Maybe I should use it for this one case then (make "colors" a function)..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.