Copy-assigning 具有不兼容分配器的无序映射

Copy-assigning an unordered map with incompatible allocators

考虑以下虚拟分配器(为示例而创建):

template<typename T> class C
{
public:
    typedef T value_type;

    C() = default;

    template<typename U>
    C(C<U> const &a)
    {}


    T* allocate(std::size_t n, T const* = nullptr)
    {
        return new T[n];
    }

    void deallocate(T* p, std::size_t n)
    {
        return;
    }

    typedef value_type       *pointer;
    typedef const value_type *const_pointer;
    typedef value_type       &      reference;
    typedef value_type const &const_reference;
    typedef std::size_t       size_type;
    typedef std::ptrdiff_t    difference_type;
    static       pointer address(reference x) { return &x; }
    static const_pointer address(const_reference x) { return &x; }
    static size_type max_size() { return std::numeric_limits<size_type>::max(); }
    template <typename U> static void destroy(U* ptr) { ptr->~U(); }
    template <typename U> struct rebind { using other = C<U>; };

    template<typename U, typename... Args>
    static void construct(U* ptr, Args&&... args) {
        new (ptr) U(std::forward<Args>(args)...);
    }
};

template<class T1, class T2>
bool operator==(C<T1> const& lhs, C<T2> const& rhs)
{
    return std::addressof(lhs) == std::addressof(rhs);
}

template<class T1, class T2>
bool operator!=(C<T1> const& lhs, C<T2> const& rhs)
{
    return !(lhs == rhs);
}

大部分代码都是样板。关键的细节是分配器的任何两个实例都将被视为不兼容 - bool operator== 总是 returns false。当我尝试将此分配器与大多数 STL 容器一起使用时,例如 std::vector 到 copy-assign 非常简单的元素,例如:

std::vector<int, C<int>> a;
a = std::vector<int, C<int>>();

一切正常,我得到了预期的行为。但是,当我做同样的事情,但使用 std::unordered_map 时,我会在我需要支持的两个平台上得到不同的行为。在 GCC 7.1 Linux 上,我继续获得预期的行为。然而,在使用 VS 2015 的 Windows 上,我在标题为 xmemory0 的 VS header 中得到一个断言失败,指出 containers incompatible for swap。请注意,用于 std::unordered_map 的代码与上面用于 std::vector:

的代码几乎相同
using B = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, C<std::pair<int const, int>>>;
B b;
b = B();

我的分配器是否存在固有问题,GCC 7.1 给我未定义的行为?如果不是,这是 VS 2015 运行时库的故障吗?如果是这样,为什么此故障仅出现在 unordered_map 中?

你不能拥有唯一拥有状态的分配器,它们 must be CopyConstructible。例如。你应该从 std::unique_ptrs 切换到 std::shared_ptrs.

你应该放松比较

template<class T1, class T2>
bool operator==(C<T1> const& lhs, C<T2> const& rhs)
{
    return /* check equality of some member of C */;
}

template<class T1, class T2>
bool operator!=(C<T1> const& lhs, C<T2> const& rhs)
{
    return !(lhs == rhs);
}

您也可能受益于遵守 Rule of zero/five,并将 propogate_on_container_copy_assignmentpropogate_on_container_move_assignmentpropogate_on_container_swap 定义为 std::true_type

关于 MSVC 在哪里出错的提示

Note: swapping two containers with unequal allocators if propagate_on_container_swap is false is undefined behavior.

这不是符合标准的分配器。分配器的所有副本,包括回弹副本,必须相互比较相等。


另外,unordered_mapvalue_typepair<const Key, Value>,所以你的例子应该使用C<pair<const int, int>>.