复制有状态内存分配器:标准库分配器语义和内部内存

7

我正在编写一组分配器,旨在用于非常高性能的环境中,因此稍微受限的使用(由编译器调节,而不是运行时错误)是可取的。我已经阅读了C++11状态分配器的语义以及符合容器的预期使用方式。

下面贴出一个简单的分配器,它只包含分配器对象内的一块内存。在C++03中,这是不合法的。

template <typename T, unsigned N>
class internal_allocator {
private:
    unsigned char storage[N];
    std::size_t cursor;
public:
    typedef T value_type;
    internal_allocator() : cursor(0) {}
    ~internal_allocator() { }

    template <typename U>
    internal_allocator(const internal_allocator<U>& other) {
        // FIXME: What are the semantics here?
    }

    T* allocate(std::size_t n) {
        T* ret = static_cast<T*>(&storage[cursor]);
        cursor += n * sizeof(T);
        if (cursor > N)
            throw std::bad_alloc("Out of objects");
        return ret;
    }
    void deallocate(T*, std::size_t) {
        // Noop!
    }
};

在C++11中,这可行吗?“复制有状态的分配器”是什么意思?由于目标容器调用源容器中所有元素的复制构造函数,因此分配器内部的内存是否必须显式复制,或者默认构造是否足够?
这引出了一个问题,以性能为最终目标,对于propagate_on_container_{copy, swap, move},合理的值是什么?select_on_container_copy_construction返回什么?
如果需要更多细节,请告诉我,因为这似乎是一个相当模糊的问题 - 至少对我来说如此=)
这种争议起源于这样的定义:当两个相同的分配器类型实例a == b返回true时,保证使用a分配的内存可以使用b释放。但似乎对于这个分配器永远不会成立。标准还规定,当分配器被复制构造时,例如A a(b),a == b保证返回true。

2
根据 https://youtu.be/0MdSJsCTRkY ,这个内存分配器是一个不好的有状态分配器的例子。看来这种类型的分配器恰恰是那种需要你拥有“非传播”分配器的情况,因为它无法以有意义的方式进行复制(在我看来,这是一种过于复杂的不好动机)。我认为把内存分配器视为指向堆的指针(带有特性和功能)是明智的。在这里也可以看到 https://dev59.com/2rHma4cB1Zd3GeqPSN4g - alfC
1个回答

6

分配器要求指出,分配器的副本必须能够释放彼此的内存,因此通常无法将内存存储在分配器对象内部。

这是有效的:

using IAllocChar = internal_allocator<char, 1024>;
IAllocChar::pointer p
IAllocChar a1;
{
  IAllocChar a2(a1);
  p = std::allocator_traits<IAllocChar>::allocate(a2, 1);
}
std::allocator_traits<IAllocChar>::deallocate(a1, p, 1)

因此,您需要在分配器对象之外存储实际内存(或仅以非常受限的方式使用它,以确保在任何东西引用其拥有的内存时,该对象不会超出范围)。

您还将遇到重新绑定internal_allocator的困难,以下代码应该执行什么操作?

using IAllocChar = internal_allocator<char, 1024>;
using IAllocInt = std::allocator_traits<IAllocChar>::rebind_alloc<int>;
IAllocChar ac;
auto pc = ac.allocate(1);  // got bored typing allocator_traits ;-)
IAllocInt  ai(ac);
auto pi = ai.allocate(1);
IAllocChar(ai).deallocate(pc, 1);
IAllocInt(ac).deallocate(pi, 1);

这对我来说很有意义。因此,这样的分配器不能以语义一致的方式进行复制。很遗憾,allocator_traits 类型不允许根据分配器的属性使容器无法复制,但我想与实现开销相比,这种功能的实用性有限。附注:感谢您在 libstdc++ 容器中处理分配器感知 =) - jared_schmitz
是的,如果您可以确保容器不可复制,即使您的分配器不符合所有要求,您也可能会摆脱它,尽管这取决于实现是否不进行不必要的内部复制(这是一个合理的假设,但不能保证)。感谢您的感谢 :) - Jonathan Wakely

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