`std::pair` `second` имеет неполный тип с деревом `unordered_map`

Я просматривал свой старый код и увидел код, использующий указатели для реализации дерева из Variant объектов. Это дерево, потому что каждый Variant может содержать unordered_map из Variant*.

Я посмотрел на код и подумал, почему он не использует просто значения, std::vector<Variant> и std::unordered_map<std::string, Variant> вместо Variant*.

Так что я пошел вперед и изменил его. Вроде все нормально, за исключением одного: я получил ошибки:

/usr/local/include/c++/6.1.0/bits/stl_pair.h:153:11: error: 'std::pair<_T1, _T2>::second' has incomplete type
       _T2 second;                /// @c second is a copy of the second object
           ^~~~~~ main.cpp:11:8: note: forward declaration of 'struct Variant'
 struct Variant
        ^~~~~~~

Поэтому я решил, что могу обмануть компилятор, заставив его отложить необходимость знать этот тип, что не сработало. либо.

Работает Не работает! (MCVE)

Я думал, что раньше это работало, но на самом деле это не так, я забыл ::type на using HideMap...

#include <vector>
#include <unordered_map>
#include <iostream>

template<typename K, typename V>
struct HideMap
{
    using type = std::unordered_map<K, V>;
};

struct Variant
{
    using array_container = std::vector<Variant>;

    // Does not work either
    using object_container = typename HideMap<std::string, Variant>::type;

    // Fails
    //using object_container = std::unordered_map<std::string, Variant>;

private:
    union Union
    {
        std::int64_t vint;
        array_container varr;
        object_container vobj;

        // These are required when there are union
        // members that need construct/destruct
        Union() {}
        ~Union() {}
    };

    Union data;
    bool weak;
};

int main()
{
    Variant v;
    std::cout << "Works" << std::endl;
}

Итак, мой вопрос: почему это работает нормально для vector, а не для unordered_map?

Если проблема заключается в невозможности использования неполных типов, есть ли способ отложить создание экземпляра unordered_map? Я действительно не хочу, чтобы каждое свойство объекта было отдельным выделением new.


person doug65536    schedule 14.05.2016    source источник
comment
HideMap делает не то, что вы думаете.   -  person T.C.    schedule 14.05.2016
comment
@Т.С. Откуда вы знаете, что я думаю, что он делает? Это вызывает двухфазный поиск, верно?   -  person doug65536    schedule 14.05.2016
comment
@TC, вы имеете в виду, что HideMap на самом деле не является неупорядоченной картой?   -  person perencia    schedule 14.05.2016
comment
Ой! Я забыл :: type!   -  person doug65536    schedule 14.05.2016
comment
@perencia Спасибо, я изменил вопрос.   -  person doug65536    schedule 14.05.2016
comment
Для коллекций Std требуются полные типы. Векторные реализации часто допускают большую свободу действий, чем требуется, потому что это следует из деталей реализации.   -  person Richard Hodges    schedule 14.05.2016
comment
Я попытался использовать частичную специализацию после Variant, чтобы попытаться создать экземпляр карты после завершения типа Variant, но это тоже не сработало.   -  person doug65536    schedule 14.05.2016


Ответы (1)


Это использует новое размещение, чтобы отложить инициализацию Union до конструктора, где Variant — полный тип. Вам нужно reinterpret_cast везде, где вам нужно использовать Union. Я приложил усилия, чтобы не было никаких нарушений строгого выравнивания.

#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <vector>

struct Variant {
    Variant();
    ~Variant();

    private:
    std::aligned_union<0, std::vector<Variant>,
                         std::unordered_map<std::string, void *>,
                         std::int64_t>::type data;
};

namespace Variant_detail {
    using array_container = std::vector<Variant>;
    using object_container = std::unordered_map<std::string, Variant>;

    union Union {
        std::int64_t vint;
        array_container varr;
        object_container vobj;

        // These are required when there are union
        // members that need construct/destruct
        Union() {}
        ~Union() {}
    };
}

Variant::Variant() {
    //make sure that std::unordered_map<std::string, Variant> is not too large
    static_assert(sizeof(std::unordered_map<std::string, Variant>) <=
                      sizeof data, "Variant map too big");
    static_assert(alignof(std::unordered_map<std::string, Variant>) <=
                      alignof(decltype(data)), "Variant map has too high alignment");
    auto &my_union = *new (&data) Variant_detail::Union;
    my_union.vint = 42;
}

Variant::~Variant() {
    reinterpret_cast<Variant_detail::Union &>(data).~Union();
}

int main() {
    Variant v;
    std::cout << "Works" << std::endl;
}
person nwp    schedule 14.05.2016
comment
С ++ 14 все в порядке, я должен был пометить его как c ++ 14, я обновлю теги. - person doug65536; 14.05.2016
comment
@nwp Спасибо! Возможно ли, что static_assert сейчас выйдет из строя? копия @T.C. - person doug65536; 16.05.2016
comment
А как насчет этого void*? Откуда такая память? new побеждает цель. Должен ли я изменить реализацию, чтобы элементы на карте ссылались на некоторые объекты Variant, содержащиеся в векторе, а карта просто индексировала их? - person doug65536; 16.05.2016
comment
@ doug65536 Теоретически std::unordered_map может специализироваться на void * или sizeof(T) фактического типа и быть больше/меньше или требовать другого выравнивания. На практике я бы не ожидал, что это когда-либо произойдет, но static_assert стоят недорого, поэтому я бы оставил их. void * — это просто некоторый тип, поэтому unordered_map компилируется, за ним нет памяти. - person nwp; 16.05.2016
comment
Я начинаю думать, что вектор, вероятно, должен быть двухсторонней очередью, поэтому итераторы не будут признаны недействительными, а объект должен быть unordered_map <reference_wrapper<Variant>, reference_wrapper<Variant>>, указывающим на двухстороннюю очередь. - person doug65536; 16.05.2016
comment
Я чувствую, что этот ответ полон неопределенного поведения. - person user202729; 06.03.2021