10

I'm trying to initialize map of maps using C++11. My compiler is VS 2013 Express.

unordered_map<EnumType, unordered_map<string, string>> substitutions = {
    {
        Record::BasementType,
        {
            { "0", "" },
            { "1", "Slab or pier" },
            { "2", "Crawl" }
        }
    },
    {
        Record::BuildingStyle,
        {
            { "0", "" },
            { "1", "Ranch" },
            { "2", "Raised ranch" }
        }
    },
    // ... and so on
};

It's compile but I'm getting breakpoint inside ntdll.dll. However simplified version of this code:

unordered_map<EnumType, unordered_map<string, string>> substitutions = {
    {
        Record::BasementType,
        {
            { "0", "" },
            { "1", "Slab or pier" },
            { "2", "Crawl" }
        }
    },
    // *nothing more*
};

works properly.

Why this doesn't work when I have more than one pair in map? How to do it better?

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
omikron
  • 2,745
  • 1
  • 25
  • 34
  • "I'm getting breakpoint inside ntdll.dll" is too vague. Is there an assertion? If so, what does the assertion say. – thelamb Oct 09 '13 at 11:07
  • But It looks like so, I'm getting breakpoint pointing to end of initialization list and behind there is only disassembly. In debug mode it's the same but I'm getting stacktrace through std::pair, std::map, (disassembly), std::_Tree few times and it ends up in file xtree line 327: _DEBUG_ERROR("map/set iterators incompatible"); – omikron Oct 09 '13 at 12:37
  • Ok then I'm afraid I can't help any more at the moment. If I have time later. What you can do it use some online compiler to see if the code runs there (e.g. using G++ or Clang++). If so it might be a VS2013 bug. Just by looking at the code I don't see what is wrong. – thelamb Oct 11 '13 at 11:50

2 Answers2

19

This is a known compiler bug, http://connect.microsoft.com/VisualStudio/feedback/details/800104/ . The compiler gets confused by temporaries in initializer lists, and can even destroy an individual object repeatedly. Because this is silent bad codegen, I've asked the compiler team to prioritize fixing this.

Stephan T. Lavavej
  • 1,781
  • 14
  • 8
  • I thought it some kind of bug. Thanks! – omikron Nov 15 '13 at 09:22
  • I think I ran into the same bug(?) with a function declared as `struct X { void foo(/*..........*/, std::string const& data = {}); }`. It crashes randomly when called with `data` defaulted, but not always. Am I right this could be the same bug? _[Also, is there a chance of a hotfix?]_ – sehe Jan 10 '14 at 11:13
  • 1
    Anyways, I think this might be a different bug. Here's the minimal reproducer in case you are interested: **[Question: (Known) compiler bug in VC12?](http://stackoverflow.com/questions/21044488/known-compiler-bug-in-vc12)** – sehe Jan 10 '14 at 12:42
  • 1
    Stephan, is there a version of the compiler with this bug fixed at the moment? It's really sad that I have to avoid all {} initialization because more often than not it causes problems with my MSVC2013 compiler – Zeks Aug 18 '14 at 11:00
  • 1
    I just hit this bug in a fully updated MSVC2013 (see question http://stackoverflow.com/questions/33553265/using-initializer-lists-with-stdmap). The compiler team obviously has not prioritized this. :) – abelenky Nov 05 '15 at 19:47
0

A workaround for this I've discovered over the years is using make_pair in the initializer list instead of the bracket initializer per pair ({ ... }):

std::unordered_map<Record, std::unordered_map<std::string, std::string>> testmap = {
    make_pair(Record::BasementType, std::unordered_map<std::string, std::string>({ { "0", "" }, { "1", "Slab or pier" }, { "2", "Crawl" } })),
    make_pair(Record::BuildingStyle, std::unordered_map<std::string, std::string>({ { "0", "" }, { "1", "Slab or pier" }, { "2", "Crawl" } })),
    // ... and so on
};

The compiler seems to be able to deal with the pairs nicely from there regardless of the issue with temporary initializers.

Note that in your case you have to explicitly cast the inner unordered_map initializer because it could be more than one stl container type being initialized like that. Alternatively you can supply template types on make_pair which has the same casting result: make_pair<Record, std::unordered_map<std::string, std::string>>(...)

Maybe this workaround helps someone still using VS2013 like me.

Vinz
  • 3,030
  • 4
  • 31
  • 52