id summary reporter owner description type status milestone component version severity resolution keywords cc 10777 unordered_map treats operator== on stateful allocators as stateless anonymous Daniel James "Hi, here is my problem: I have a stateful allocator that has a bunch of stuff inside of it. It can be move-assigned. The problem is that boost::unordered_map does this when you have propagate_on_container_move_assign typedeffed to true_type: {{{ my_alloc a; my_alloc b; a = std::move(b); ASSERT(a == b); }}} That assert can obviously never be true if my_alloc is stateful, but this sequence of events currently happens in boost::unordered_map. Here is an allocator that shows the problem: {{{ #pragma once #include #include template struct plalloc { typedef T value_type; plalloc() = default; template plalloc(const plalloc &) {} plalloc(const plalloc &) {} plalloc & operator=(const plalloc &) { return *this; } plalloc(plalloc &&) = default; plalloc & operator=(plalloc &&) = default; typedef std::true_type propagate_on_container_copy_assignment; typedef std::true_type propagate_on_container_move_assignment; typedef std::true_type propagate_on_container_swap; bool operator==(const plalloc & other) const { return this == &other; } bool operator!=(const plalloc & other) const { return !(*this == other); } T * allocate(size_t num_to_allocate) { if (num_to_allocate != 1) { return static_cast(::operator new(sizeof(T) * num_to_allocate)); } else if (available.empty()) { // first allocate 8, then double whenever // we run out of memory size_t to_allocate = 8 << memory.size(); available.reserve(to_allocate); std::unique_ptr allocated(new value_holder[to_allocate]); value_holder * first_new = allocated.get(); memory.emplace_back(std::move(allocated)); size_t to_return = to_allocate - 1; for (size_t i = 0; i < to_return; ++i) { available.push_back(std::addressof(first_new[i].value)); } return std::addressof(first_new[to_return].value); } else { T * result = available.back(); available.pop_back(); return result; } } void deallocate(T * ptr, size_t num_to_free) { if (num_to_free == 1) { available.push_back(ptr); } else { ::operator delete(ptr); } } // boilerplate that shouldn't be needed, except // libstdc++ doesn't use allocator_traits yet template struct rebind { typedef plalloc other; }; typedef T * pointer; typedef const T * const_pointer; typedef T & reference; typedef const T & const_reference; template void construct(U * object, Args &&... args) { new (object) U(std::forward(args)...); } template void construct(const U * object, Args &&... args) = delete; template void destroy(U * object) { object->~U(); } private: union value_holder { value_holder() {} ~value_holder() {} T value; }; std::vector> memory; std::vector available; }; }}} And here is a sequence of events with which you can get it: {{{ boost::unordered_map, std::equal_to, plalloc> a = { { 1, 2 } }; boost::unordered_map, std::equal_to, plalloc> b = { { 3, 4 } }; boost::unordered_map, std::equal_to, plalloc> c = { { 5, 6 } }; a = std::move(b); a = c; }}}" Bugs closed To Be Determined unordered Boost 1.56.0 Problem fixed