如何解决嵌套容器的自定义分配器
我正在研究自定义分配器。到目前为止,我已经尝试处理简单的容器:std::list、std::vector、std::basic_string 等...
#include <memory>
template <typename T>
class StaticBufferAlloc : std::allocator<T>
{
private:
T *memory_ptr;
std::size_t memory_size;
public:
typedef std::size_t size_type;
typedef T *pointer;
typedef T value_type;
StaticBufferAlloc(T *memory_ptr,size_type memory_size) : memory_ptr(memory_ptr),memory_size(memory_size) {}
StaticBufferAlloc(const StaticBufferAlloc &other) throw() : memory_ptr(other.memory_ptr),memory_size(other.memory_size){};
pointer allocate(size_type n,const void *hint = 0) { return memory_ptr; } // when allocate return the buffer
void deallocate(T *ptr,size_type n) {} // empty cause the deallocation is buffer creator's responsability
size_type max_size() const { return memory_size; }
};
我正在以这种方式使用它:
using inner = std::vector<int,StaticBufferAlloc<int>>;
int buffer[201];
auto alloc1 = StaticBufferAlloc<int>(&buffer[100],50);
inner v1(0,alloc1);
assert(v1.size() == 0);
const int N = 10;
// insert 10 integers
for (size_t i = 0; i < N; i++) {
v1.push_back(i);
}
assert(v1.size() == N);
到目前为止一切都很好,当我增加 N 超过它抛出的最大缓冲区大小时,这是预期的。
现在,我正在尝试使用嵌套容器。简而言之,我试图拥有一个向量(矩阵)的向量,其中父向量及其所有底层元素(即向量,即容器)共享相同的静态缓冲区进行分配。看起来 scoped_allocator 可以解决我的问题。
using inner = std::vector<int,StaticBufferAlloc<int>>;
using outer = std::vector<inner,std::scoped_allocator_adaptor<StaticBufferAlloc<inner>>>;
int buffer[201];
auto alloc1 = StaticBufferAlloc<int>(&buffer[100],50);
auto alloc2 = StaticBufferAlloc<int>(&buffer[150],alloc1);
inner v2(0,alloc2);
assert(v1.size() == 0);
assert(v2.size() == 0);
const int N = 10;
// insert 10 integers
for (size_t i = 0; i < N; i++)
{
v1.push_back(i);
v2.push_back(i);
}
assert(v1.size() == N);
assert(v2.size() == N);
outer v // <- how to construct this vector with the outer buffer?
v.push_back(v1);
v.push_back(v2);
...
我的问题是如何使用静态缓冲区在其构造函数调用中初始化外部向量?
解决方法
在 C++11/C++14 中创建作用域分配器有点挑战。所以我选择了一个在 C++17 中引入的非常现代的解决方案。我没有实现分配器,而是使用了 polymorphic_allocator。多态分配器是作用域分配器,标准容器会自动将分配器传递给子对象。
基本上,这个想法是使用多态分配器并用 monotonic_buffer_resource 注入它。 monotonic_buffer_resource 可以用 memory resource 初始化。
编写自定义内存资源非常简单:
class custom_resource : public std::pmr::memory_resource
{
public:
explicit custom_resource(std::pmr::memory_resource *up = std::pmr::get_default_resource())
: _upstream{up}
{
}
void *do_allocate(size_t bytes,size_t alignment) override
{
return _upstream; //do nothing,don't grow just return ptr
}
void do_deallocate(void *ptr,size_t bytes,size_t alignment) override
{
//do nothing,don't deallocate
}
bool do_is_equal(const std::pmr::memory_resource &other) const noexcept override
{
return this == &other;
}
private:
std::pmr::memory_resource *_upstream;
};
使用起来更简单:
std::byte buffer[512];
custom_resource resource;
std::pmr::monotonic_buffer_resource pool{std::data(buffer),std::size(buffer),&resource};
std::pmr::vector<std::pmr::vector<int>> outer(&pool)
需要注意的是,std::pmr::vector<T>
只是 std::vector<T,polymorphic_allocator>
。
有用的资源:
- CppCon 2017: Pablo Halpern “Allocators: The Good Parts”
- C++ Weekly - Ep 222 - 3.5x Faster Standard Containers With PMR
- Purpose of scoped allocator
std::pmr 很酷,但它需要现代版本的 gcc 才能运行(9+)。幸运的是,Reddit 上到处都是善良的陌生人。可以在 here 中找到 C++14 解决方案。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。