Вложенные списки инициализаторов hash_map

Я пытаюсь использовать std::hash_map для определения вложенного объекта. Я использую Visual Studio 2013.

Проблемы начинаются, когда я пытаюсь использовать вложенные литералы инициализации. Я сократил свой подход к следующему:

enum ENUM1 {
    ENUM1_A,
    ENUM1_B,
};

enum ENUM2 {
    ENUM2_A,
    ENUM2_B
};

enum ENUM3 {
    ENUM3_A,
    ENUM3_B
};

std::hash_map<ENUM1, std::hash_map<ENUM2, std::hash_map<ENUM3, int>>> A = {
    {
        ENUM1_A, {
            {   
                ENUM2_A, {
                    { ENUM3_A, 123 },
                    { ENUM3_B, 45 },
                },
            },
            {
                ENUM2_B, {
                    { ENUM3_A, 733 },
                    { ENUM3_B, 413 },
                }
            }
        }
    }
};

Компилятор не жалуется, но при выполнении выдает нарушение прав доступа и следующий стек вызовов:

    ***.exe!std::list<std::pair<enum ENUM3 const ,int>,std::allocator<std::pair<enum ENUM3 const ,int> > >::clear() Line 1503   C++
    ***.exe!std::list<std::pair<enum ENUM3 const ,int>,std::allocator<std::pair<enum ENUM3 const ,int> > >::_Tidy() Line 1884   C++
    ***.exe!std::list<std::pair<enum ENUM3 const ,int>,std::allocator<std::pair<enum ENUM3 const ,int> > >::~list<std::pair<enum ENUM3 const ,int>,std::allocator<std::pair<enum ENUM3 const ,int> > >() Line 1096  C++
    ***.exe!std::_Hash<stdext::_Hmap_traits<enum ENUM3,int,stdext::hash_compare<enum ENUM3,std::less<enum ENUM3> >,std::allocator<std::pair<enum ENUM3 const ,int> >,0> >::~_Hash<stdext::_Hmap_traits<enum ENUM3,int,stdext::hash_compare<enum ENUM3,std::less<enum ENUM3> >,std::allocator<std::pair<enum ENUM3 const ,int> >,0> >() Line 409 C++
    ***.exe!stdext::hash_map<enum ENUM3,int,stdext::hash_compare<enum ENUM3,std::less<enum ENUM3> >,std::allocator<std::pair<enum ENUM3 const ,int> > >::~hash_map<enum ENUM3,int,stdext::hash_compare<enum ENUM3,std::less<enum ENUM3> >,std::allocator<std::pair<enum ENUM3 const ,int> > >() C++
    ***.exe!std::pair<enum ENUM2 const ,stdext::hash_map<enum ENUM3,int,stdext::hash_compare<enum ENUM3,std::less<enum ENUM3> >,std::allocator<std::pair<enum ENUM3 const ,int> > > >::~pair<enum ENUM2 const ,stdext::hash_map<enum ENUM3,int,stdext::hash_compare<enum ENUM3,std::less<enum ENUM3> >,std::allocator<std::pair<enum ENUM3 const ,int> > > >()  C++
    ***.exe!`dynamic initializer for 'A''() Line 76 C++

К сожалению, я очень новичок в C++ и сам не знаю, что с этим делать.

Я видел следующий вопрос, где ответ ссылается на ошибку компилятора, но в связанном отчете об ошибке конкретно упоминается, что эта проблема связана со строковыми литералами.

Что я делаю не так? Несколько не связанный, что мешает вычислить объект с таким постоянным значением во время компиляции? Есть ли лучший способ определить JSON-подобные объекты в коде?


person user1421750    schedule 06.05.2014    source источник


Ответы (1)


В MSVC2013 есть многочисленные (ну, по крайней мере, 3) назойливые баги, связанные со списками инициализаторов и юниформ-инициализацией.

Обновление Согласно комментариям, эта конкретная ошибка была устранена в VS13 Update 2.

К сожалению, совет состоит в том, чтобы... пока держаться подальше от многих из них. Я придерживаюсь следующего правила:

  • для любого типа, конструктор которого принимает список инициализаторов (как и все стандартные контейнеры) всегда явно называть тип (IOW не использует синтаксис анонимного унифицированного инициализатора)

Подключить ошибки:

Что бы это ни стоило, следующий код стандартной библиотеки является честной игрой на gcc/clang: Колиру

И вот как я бы порекомендовал сформулировать его для MSVC (я не могу проверить это прямо сейчас, так как у меня нет окна под рукой):

#include <unordered_map>

enum ENUM1 { ENUM1_A, ENUM1_B }; 
enum ENUM2 { ENUM2_A, ENUM2_B }; 
enum ENUM3 { ENUM3_A, ENUM3_B }; 
namespace std {
    template <> struct hash<ENUM1> : std::hash<int> {};
    template <> struct hash<ENUM2> : std::hash<int> {};
    template <> struct hash<ENUM3> : std::hash<int> {};
}

int main() {
    using Map3 = std::unordered_map<ENUM3, int>;
    using Map2 = std::unordered_map<ENUM2, Map3>;
    std::unordered_map<ENUM1, Map2> A = {
        {
            ENUM1_A, Map2 {
                {
                    ENUM2_A, Map3 {
                        { ENUM3_A, 123 },
                        { ENUM3_B, 45 },
                    },
                },
                {
                    ENUM2_B, Map3 {
                        { ENUM3_A, 733 },
                        { ENUM3_B, 413 },
                    }
                }
            }
        }
    };
}
person sehe    schedule 06.05.2014
comment
Добавлен образец явного построения имени типа для полноты картины. - person sehe; 07.05.2014
comment
Спасибо за подробный ответ. Для справки, предложенный вами обходной путь все еще не работает. - person user1421750; 07.05.2014
comment
@user1421750 user1421750 О, это немного удивительно. Вы использовали расширение std::unordered_map или hash_map? - person sehe; 07.05.2014
comment
Я использовал std::unordered_map. Однако я только что обновился до релиз-кандидата VS13 Update 2, и я очень рад сообщить, что ошибка была исправлена! Мой исходный код тоже работает. Может быть, вы можете обновить свой ответ с этим. - person user1421750; 07.05.2014