c++ - 在带有 std::unordered_map 的 std::scoped_allocator_adaptor 中使用自定义分配器

标签 c++ stl allocator libc++

我正在尝试将一个简单的内存池分配器与 std::unordered_map 一起使用。我在 std::stringstd::vector 中似乎成功地使用了同一个分配器。我希望 unordered_map(和 vector )中包含的项目也使用此分配器,因此我将我的分配器包装在 std::scoped_allocator_adaptor 中。

简化定义集:

template <typename T>
using mm_alloc = std::scoped_allocator_adaptor<lake_alloc<T>>;

using mm_string = std::basic_string<char, std::char_traits<char>, mm_alloc<char>>;
using mm_vector = std::vector<mm_string, mm_alloc<mm_string>>;
using mm_map = std::unordered_map<mm_string, mm_vector, std::hash<mm_string>, std::equal_to<mm_string>, mm_alloc<std::pair<mm_string, mm_vector>>>;

这样初始化:

lake pool;
mm_map map { mm_alloc<std::pair<mm_string, mm_vector>>{pool} };

lake_alloc 与其余的迭代器代码一起显示在下面。我在 Clang 3.3 中遇到的错误是它不能将其 allocator_type(在本例中是字符串到 vector 对的 mm_alloc)分配给它自己的 __pointer_allocator。这是用于 HashMap 实现的内部类型。部分错误输出如下:

lib/c++/v1/__hash_table:848:53: error: no matching conversion for functional-style
      cast from 'const allocator_type' (aka 'const std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<std::__1::pair<std::__1::basic_string<char,
      std::__1::char_traits<char>, std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, std::__1::vector<std::__1::basic_string<char,
      std::__1::char_traits<char>, std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >,
      std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<std::__1::basic_string<char, std::__1::char_traits<char>,
      std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, krystal::lake> > > >, krystal::lake> >') to '__pointer_allocator' (aka
      'std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<std::__1::__hash_node<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>,
      std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>,
      std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<std::__1::basic_string<char,
      std::__1::char_traits<char>, std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, krystal::lake> > > >, void *> *, krystal::lake> >')
    : __bucket_list_(nullptr, __bucket_list_deleter(__pointer_allocator(__a), 0)),
                                                    ^~~~~~~~~~~~~~~~~~~~~~~

GCC 4.7.1 在其 HashMap 内部结构中给我一个类似的错误,很明显我做错了,但这是我第一次尝试 STL 中的分配器,我不知所措。

接下来是自定义分配器,它是一个简单的实现,其中有一些漏洞,但这个版本在包含几兆 vector 和字符串数据的包含测试用例中运行良好。

#include <cstddef>
#include <memory>
#include <scoped_allocator>

class lake {
    const size_t block_size_;
    mutable std::vector<std::unique_ptr<uint8_t[]>> blocks_;
    mutable uint8_t *arena_, *pos_;

    static constexpr const size_t DefaultBlockSize = 48 * 1024;

    void add_block(size_t of_size) const {
        blocks_.emplace_back(new uint8_t[of_size]);
        pos_ = arena_ = blocks_.back().get();
    }

    inline void add_block() const { add_block(block_size_); }

public:
    lake(const size_t block_size)
    : block_size_ {block_size}
    {
        add_block();
    }
    lake() : lake(DefaultBlockSize) {}

    void* allocate(size_t n) const {
        if (pos_ + n - arena_ > block_size_) {
            if (n > block_size_)
                add_block(n); // single-use large block
            else
                add_block();
        }

        auto result = pos_;
        pos_ += n;
        return result;
    }

    void deallocate(void* p, size_t n) const {
    }
};


template <typename T, typename Alloc>
class krystal_alloc {
    const Alloc* allocator_;

public:
    using value_type = T;
    using size_type = size_t;
    using difference_type = ptrdiff_t;
    using pointer = T*;
    using const_pointer = const T*;
    using reference = T&;
    using const_reference = const T&;

    template <typename U>
    struct rebind { typedef krystal_alloc<U, Alloc> other; };

    krystal_alloc() : allocator_{ new Alloc() } {} // not used
    krystal_alloc(const Alloc& alloc) : allocator_{ &alloc } {}

    pointer address(reference v) {
        return 0;
    }

    const_pointer address(const_reference v) {
        return 0;
    }

    size_type max_size() const {
        return static_cast<size_type>(-1) / sizeof(value_type);
    }

    pointer allocate(size_type n) {
        return static_cast<pointer>(allocator_->allocate(sizeof(T) * n));
    }

    void deallocate(pointer p, size_type n) {
        allocator_->deallocate(p, n);
    }
};

template <typename T, typename Alloc, typename U>
inline bool operator==(const krystal_alloc<T, Alloc>&, const krystal_alloc<U, Alloc>) { return true; }

template <typename T, typename Alloc, typename U>
inline bool operator!=(const krystal_alloc<T, Alloc>&, const krystal_alloc<U, Alloc>) { return false; }


// -- standard usage
template <typename T>
using lake_alloc = krystal_alloc<T, lake>;

最佳答案

我认为基本错误是您的 krystal_alloc 缺少“转换构造函数”:

template <class U>
    krystal_alloc(const krystal_alloc<U, Alloc>& u)
        : allocator_(u.allocator_) {}

我不确定我是否正确实现了它,这只是我最好的猜测。你需要一个 friend 声明才能完成这项工作:

template <class U, class A> friend class krystal_alloc;

我还建议您在 unordered_map 的分配器中将“const”添加到 key_type:

using mm_map = std::unordered_map<mm_string, mm_vector, std::hash<mm_string>,
                              std::equal_to<mm_string>,
                              mm_alloc<std::pair<const mm_string, mm_vector>>>;

而且我认为您可以在内部容器上使用lake_alloc 而不是mm_alloc。你的例子为我编译了两种方式。我没有测试它的运行时行为。

关于c++ - 在带有 std::unordered_map 的 std::scoped_allocator_adaptor 中使用自定义分配器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17517454/

相关文章:

c++ - C++ 隐式复制和移动构造函数背后的基本原理?

c++ - 一维数组的操作

c++ - 如何使用 Intel TBB 并行运行一个函数两次?

c++ - uninitialized_copy/fill(In first, In last, For dest, A &a) 是 c++ 标准中的疏忽吗?

c++ - 为什么我的代码不能与自定义分配器一起使用?

c++ - 用于字符串的容量值

c++ - 如何通过cpp程序将参数传递给bash脚本

c++ - C++中的堆排序

c++ - 如何用STL重构这段代码?

c++ - 插入集合时出现段错误