Search code examples
c++c++11initializer-listlist-initialization

(VS2015) Attempting to fill a static map with data from initializer list


I have a class sorta like:

class Object {
public:
    struct Flag {
        const uint32_t bit = 0;
        const wchar_t* name = L"";
        const wchar_t sign = L"";
    }

    static std::map<const char*, Flag> Flags;
}

I'm currently on VS2015, but want to support clang and gcc (the latest). My problem is, I can't figure out how to actually initialize that map with data.

I tried putting it inline, like:

static std::map<const char*, Flag> Flags = {
    { "FOO1",       { 0, L"FOO1",       L'A' } },
    { "FOO2",       { 1, L"FOO3",       L'B' } },
    { "FOO3",       { 2, L"FOO3",       L'C' } }
}

But that complained that only const integral types can be in-class. Okay! So I just left it as the declaration in the class definition (as shown in the first code snippet), and put this in the associated cpp:

static std::map<const char*, Object::Flag> Object::Flags = {
    { "FOO1",       { 0, L"FOO1",       L'A' } },
    { "FOO2",       { 1, L"FOO3",       L'B' } },
    { "FOO3",       { 2, L"FOO3",       L'C' } }
}

Now this complains that:

error C2440: 'initializing': cannot convert from 'initializer list' to 'std::map,std::allocator>>'

The thing is, I could've sworn I've had this working, so I'm thinking I must have the syntax wrong. If not, clearly I'm missing how to load a static map into a classes namespace.


Solution

  • According to the C++11 standard, Flag is not an aggregate due to the presence of the brace-or-equal-initializers (aka default member initializers), so attempting to use aggregate initialization to initialize it fails. C++14 removed this restriction, so Flag is considered an aggregate according to that version of the standard, and your code is valid.

    Here's a much simpler version of your example that fails to compile with -std=c++11, but successfully compiles with -std=c++14.

    #include <stdint.h>
    
    struct Flag {
        const uint32_t bit = 0;
        const wchar_t* name = L"";
        const wchar_t sign = L' ';
    };
    
    int main()
    {
        Flag f{ 0U, L"FOO1", L'A' };
    }
    

    Live demo.

    VS2015 still has the C++11 behavior, so your options are to either remove the default member initializers (thus making Flag an aggregate), or provide a constructor for Object::Flag.