簡體   English   中英

標准::地圖<std::variant, std::variant>從 KeyType 到 ValueType 的映射

[英]std::map<std::variant, std::variant> with mapping from KeyType to ValueType

我正在嘗試使用從KeyTypeMappedType的映射來創建變體到變體的std::map

這適用於大約 100 種左右的密鑰類型: https : //coliru.stacked-crooked.com/a/3959534e4fa38caa 當我嘗試使用 200 個鍵類型進行編譯時,GCC 想了一會兒,然后放棄了錯誤:

g++: internal compiler error: Killed (program cc1plus)

我相信可能是參數包太大的問題。 是否有更具可擴展性的解決方案?

我在下面列出了解決方案的主要部分:

template<typename Key, typename T>
struct TypeMap {
    using KeyType = Key;
    using MappedType = T;
};

template<typename>
constexpr bool false_v = false;

// End case
template<typename Key, typename...>
struct MappedTypeForKey {
    static_assert(false_v<Key>, "Container mapping for key type not found");
};

// Recursive case
template<typename Key, typename MapKey, typename T, typename... TypeMaps>
struct MappedTypeForKey<Key, TypeMap<MapKey, T>, TypeMaps...> : MappedTypeForKey<Key, TypeMaps...> {
};

// Match case
template<typename Key, typename T, typename... TypeMaps>
struct MappedTypeForKey<Key, TypeMap<Key, T>, TypeMaps...> {
    using Type = T;
};

template<typename... TypeMaps>
struct VariantTypeMapImpl {
    using KeyType = std::variant<typename TypeMaps::KeyType...>;
    using MappedType = std::variant<typename TypeMaps::MappedType...>;
    template<typename Key>
    using TypeForKey = typename MappedTypeForKey<Key, TypeMaps...>::Type;
};

// This is the key part of the code, allowing a developer to extend the map variants
// at the same time as defining the type mappings:
using VariantTypeMap = VariantTypeMapImpl<
    TypeMap<FrogKey, Frog>,
    TypeMap<CatKey, Cat>
>;

class VariantMap {
  public:
    size_t size() const { return map_.size(); }

    template<typename Key>
    void insert(Key key, VariantTypeMap::TypeForKey<Key> value)
    {
        map_.emplace(std::move(key), std::move(value));
    }

    template<typename Key>
    const VariantTypeMap::TypeForKey<Key>& get(const Key& key) const
    {
        return std::get<VariantTypeMap::TypeForKey<Key>>(map_.at(key));
    }

  private:
    std::map<VariantTypeMap::KeyType, VariantTypeMap::MappedType> map_;
};

如果實際上為所有Key類型實例化了insertget並且它們都是不同的,則您需要MappedTypeForKey二次方實例化,因為每次查找將花費映射對數量的平均線性時間。 最終 GCC 將耗盡內存來存儲這些實例並崩潰。

相反,您可以通過對基類的引用使用模板參數推導規則來獲得類型之間的映射。 這確實只需要總共線性量的編譯時內存,如果編譯器已經很好地實現了基類查找,那么每次查找最多需要對數時間,因此整個程序將有希望具有次二次編譯時間和映射對數量的內存復雜度:

template<typename... TypeMaps>
struct VariantTypeMapImpl {
    using KeyType = std::variant<typename TypeMaps::KeyType...>;
    using MappedType = std::variant<typename TypeMaps::MappedType...>;

    struct map : TypeMaps... {
        template<typename Key, typename Mapped>
        static auto lookup(const TypeMap<Key, Mapped>&) -> Mapped;
    };

    template<typename Key>
    using TypeForKey = decltype(map::template lookup<Key>(map{}));
};

這要求所有鍵都不同。 否則你會收到錯誤,抱怨不明確的調用或重復的基類。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM