我正在尝试创建从std::map
到KeyType
的映射的变体MappedType
到变体。
此选项最多可用于大约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_;
};
如果实际上对于所有MappedTypeForKey
类型都实例化了insert
或get
,则需要二次方Key
的多个实例化。最终,GCC将耗尽内存来存储这些实例化和崩溃。
相反,您可以通过使用基类的模板参数推导规则来映射到类型。如果编译器已很好地实现了此基类查找,则将花费子线性时间,从而使整个程序有望不再具有二次编译时间和内存复杂性:
template<typename... TypeMaps>
struct VariantTypeMapImpl {
using KeyType = std::variant<typename TypeMaps::KeyType...>;
using MappedType = std::variant<typename TypeMaps::MappedType...>;
template<typename, typename>
struct entry {};
struct MappedTypeForKey
: entry<TypeMaps::KeyType, KeyType::MappedType>... {
template<typename Key, typename Mapped>
static auto lookup(const entry<Key, Mapped>&) -> Mapped;
};
template<typename Key>
using TypeForKey = decltype(MappedTypeForKey::lookup<Key>(MappedTypeForKey{}));
};