使用std::type_index作为map的值

17

我正在尝试创建一个 std::unordered_map,其中是一个 std::type_index。以下代码片段可行:

std::unordered_map<std::type_index, int> workingMap;
workingMap[typeid(int)] = 1;
workingMap[typeid(char)] = 2;

但是这个程序并未运行,而是抛出了一个错误:

std::unordered_map<int, std::type_index> failingMap;
failingMap[1] = typeid(int);
failingMap[2] = typeid(char);
CS2512: 'std::type_index::type_index': 没有适当的默认构造函数可用。我不完全理解这个错误,这些示例中的构造函数有什么区别?是否可以创建一个映射表,其中typeid(..)是值而不是键?

9
通过执行failingMap[1],如果在映射中未找到默认条目,则创建一个默认条目。例如,可以使用::try_emplace函数。 - Sopel
2个回答

21

问题在于 operator[],而不是 map 的实际使用。问题在于如果没有找到键,则 operator[] 会分配一个默认值并返回对该值的可修改引用,这在 std::type_index 中是不可能的。您可以使用 emplaceinserttry_emplace 或其他任何不需要默认构造函数的修改器。


2

当然,我们可以为type_info创建一个可空的包装器。

#include <typeindex>
#include <functional>
#include <unordered_map>
#include <cassert>


struct nullable_type_index
{
    constexpr nullable_type_index() : ptr_(nullptr) {}
    constexpr nullable_type_index(std::type_info const& ti) : ptr_(std::addressof(ti)) {}

    constexpr operator bool() const
    {
        return bool(ptr_);
    }

    // I have avoided implicit conversion, but it could probably work
    // without any surprises.
    std::type_info const& get_type_info() const {
        assert(ptr_);
        return *ptr_;
    }

    constexpr bool operator==(nullable_type_index const& other) const {
        return ptr_ && other.ptr_ 
        ? *ptr_ == *other.ptr_
        : ptr_ == other.ptr_;
    }

private:
    std::type_info const* ptr_;
};

std::size_t hash_value(const nullable_type_index& nti)
{
    return nti ? 0 : nti.get_type_info().hash_code();
}

bool operator==(nullable_type_index const& l, std::type_info const& r)
{
    return l == nullable_type_index(r);
}

bool operator==(std::type_info const& l, nullable_type_index const& r)
{
    return nullable_type_index(l) == r;
}

namespace std {

    template<>
    struct hash<nullable_type_index>
    {
        std::size_t operator()(nullable_type_index const& arg) const {
            return hash_value(arg);
        }
    };
}

int main()
{
    std::unordered_map<std::type_index, int> workingMap;
    workingMap[typeid(int)] = 1;
    workingMap[typeid(char)] = 2;    

    std::unordered_map<int, nullable_type_index> failingMap;
    failingMap[1] = typeid(int);
    failingMap[2] = typeid(char);
}

当然,现在我们有std::optional...
int main()
{
    std::unordered_map<std::type_index, int> workingMap;
    workingMap[typeid(int)] = 1;
    workingMap[typeid(char)] = 2;    

    std::unordered_map<int, std::optional<std::type_index>> failingMap;
    failingMap[1] = typeid(int);
    failingMap[2] = typeid(char);
}

这是一个关于 std::optional 的好答案,而且 C++11 的后移版本只需要一个头文件(还有一个适用于 C++03 的 Boost 版本)。我真的认为可空版本有些过度,因为已经存在很好的替代方案了。不过,回答得非常好。 - Alex Huszagh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接