-1

This subject might be already treated but I can't find solution to this problem. I declare a static const std::string[] member in a class like this:

The .h:

class MyClass
{
private:
    static const std::string cArray[aNumber];

    //other stuff like ctors & all
}

The .cpp

const std::string MyClass::cArray[] = {"", "ini", "txt", "bmp"};

This class is included in another header in wich I declare static const array[] of MyClass. The problem is: when these arrays are builded, m_cArray contains empty strings which I use to fix stuffs in other static array.

I saw some threads on static initialization order issue but I didn't found a helpful answer.

Suggestions are welcome. Thanks

gfache
  • 606
  • 6
  • 15

3 Answers3

3

You forgot to specify that cArray belongs to the class MyClass during the definition of your static member :

const std::string MyClass::cArray[] = {"", "ini", "txt", "bmp"};
//                ^^^^^^^^^^^^^^^

EDIT:

Your problem seems to be that the order of initialization of static members is undefined in C++.

As it is said here, the the most elegant way around it is to wrap the initialization in a function :    

class MyClass
{
private:
    static std::string* Array()
    {
        static std::string cArray[aNumber] = {"", "ini", "txt", "bmp"};
        return cArray;
    }
};

And you access your array with:

MyClass::Array();

EDIT: You corrected this mistake in your example

Maybe your other mistake could be that you named your member cArray in the class declaration:

class MyClass
{
private:
    static const std::string cArray[aNumber];
    //                       ^^^^^^
};

and m_cArray in the member definition:

const std::string m_cArray[] = {"", "ini", "txt", "bmp"};
//                ^^^^^^^^

I corrected this error on my first sample of code.

Community
  • 1
  • 1
Pierre Fourgeaud
  • 14,290
  • 1
  • 38
  • 62
  • Also, it's called `cArray`, not `m_cArray`. – Lightness Races in Orbit Aug 30 '13 at 12:28
  • @LightnessRacesinOrbit Thanks for pointing this out. I didn't see this error because of my habit to name the member like that. – Pierre Fourgeaud Aug 30 '13 at 12:29
  • Anyway, apart from the mistake due to my chronic alcoholism, the problem remains :). When I use some methods which use cArray in following header, I get empty strings in place of initial content :/ – gfache Aug 30 '13 at 13:03
  • @Weldryn Look at [**this example**](http://ideone.com/wzEOET). It seems to work fine... And it works at home too with the class declaration in a different header. – Pierre Fourgeaud Aug 30 '13 at 13:09
  • @PierreFourgeaud OP's problem seems to come from the static initialization order fiasco, so I very much doubt you can reproduce his problem with a single translation unit. ;) – syam Aug 30 '13 at 13:14
  • @syam It will depend also on the compiler because, even with another Cpp and header, I'm still having the correct result. Effectively it seems to be a way more complex than that. – Pierre Fourgeaud Aug 30 '13 at 13:17
  • Yes, that's almost what I've done. But it doesn't work for me :/ I precise that I don't use C++11. My compiler is VS2008 – gfache Aug 30 '13 at 13:19
  • @PierreFourgeaud Indeed, that's the whole problem with static initialization of global variables. It depends on the compiler, and sometimes even on the linking order of the various translations units involved. Which is the very reason why it's utterly unreliable. – syam Aug 30 '13 at 13:19
  • @Weldryn Post edited with a work-around to solve your problem. – Pierre Fourgeaud Aug 30 '13 at 13:30
2

You need to say which class cArray is a member of when you initialise it

const std::string MyClass::cArray[] = {"", "ini", "txt", "bmp"};
simonc
  • 41,632
  • 12
  • 85
  • 103
1

Your problem seems indeed to stem from the infamous static initialization order fiasco.

Basically, when you have a static variable X in one translation unit, referring to another static variable Y in a second translation unit, then your program has 50/50 chances of misbehaving. In order to behave properly, Y should be initialized before X but C++ doesn't enforce that.

As far as I know, the only proper way to handle this is to use function-level static variables, which ensure that m_array will be initialized on first call of MyClass::array() (and in C++11 this initialization is even guaranteed to be thread-safe):

struct MyClass {
    static const size_t arraySize = 4;

    // This function could be defined in a .cpp rather than inline
    // I only put it inside the class for compactness/readability reasons
    static const std::string* array() {
        static const std::string m_array[arraySize] = {"", "ini", "txt", "bmp"};
        return m_array;
    }
};

// In some other file
struct OtherClass {
    // This function could be defined in a .cpp rather than inline
    static void whatever() {
        do_something_with(MyClass::array());
    }
};

In other words, you should avoid declaring static global or class variables (unless you are absolutely sure they can be resolved to compile-time constants, like arraySize above), but wrap them at function-level inside static functions.


As a side note, this idiom makes it much easier for you to use a proper container rather than a legacy C array, eg. std::vector or, if you are using C++11, std::array:

// C++03
struct MyClass {
    // This function could be defined in a .cpp rather than inline
    static const std::vector<std::string>& data() {
        static std::vector<std::string> m_data;
        if (m_data.empty()) {
            m_data.push_back("");
            m_data.push_back("ini");
            m_data.push_back("txt");
            m_data.push_back("bmp");
        }
        return m_data;
    }
};

// C++11
struct MyClass {
    using Container = std::vector<std::string>;
    // or
    // using Container = std::array<std::string, 4>;

    // This function could be defined in a .cpp rather than inline
    static const Container& data() {
        static const Container m_data = {"", "ini", "txt", "bmp"};
        return m_data;
    }
};
syam
  • 14,701
  • 3
  • 41
  • 65
  • I do understand what you mean but not the code you wrote (the first part, in any case). My compiler is VS2008 so I can't use `array` type. The second part is much comprehensible for me: you use a factory function if I'm not mistaken? – gfache Aug 30 '13 at 13:24
  • @Weldryn I was using C++11 features, but I edited it when I saw you were using C++03 so you should be good to go now. As to the second part, that's not a factory function (factory means, create new objects each time), it's the "*construct on first use*" idiom that is enabled by function-level static variables. There is only one instance of `m_data`, just like in your original code, but it is initialized on the first call of the function so the fiasco you're victim of cannot happen. – syam Aug 30 '13 at 13:27