简体   繁体   English

我可以假设分配器不直接保存它们的内存池(因此可以复制)吗?

[英]Can I assume allocators don't hold their memory pool directly (and can therefore be copied)?

I'm writing a container and would like to permit the user to use custom allocators, but I can't tell if I should pass allocators around by reference or by value. 我正在编写一个容器,并希望允许用户使用自定义分配器,但我不知道是否应该通过引用或值传递分配器。

Is it guaranteed (or at least, a reasonable assumption to make) that an allocator object will not contain its memory pool directly, and hence it would be OK to copy an allocator and expect the memory pools of the allocators to be cross-compatible? 是否保证(或者至少是合理的假设)分配器对象不会直接包含其内存池,因此可以复制分配器并期望分配器的内存池交叉兼容? Or do I always need to pass allocators by reference? 或者我是否总是需要通过引用传递分配器?

(I have found that passing by reference harms performance by a factor of > 2 because the compiler starts worrying about aliasing, so it makes a whether or not I can rely on this assumption.) (我发现通过引用传递会损害性能> 2因为编译器开始担心别名,所以它是否可以依赖于这个假设。)

The old C++ standard makes requirements for a standard-compliant allocator: These requirements include that if you have Alloc<T> a, b , then a == b , and you can use b to deallocate things that were allocated with a . 旧的C ++标准对符合标准的分配器提出了要求:这些要求包括如果你有Alloc<T> a, b ,那么a == b ,你可以使用b来解除分配用a分配的东西。 Allocators are fundamentally stateless . 分配器基本上是无国籍的


In C++11, the situation got a lot more involved, as there is now support for stateful allocators. 在C ++ 11中,情况变得更加复杂,因为现在支持有状态分配器。 As you copy and move objects around, there are specific rules whether one container can be copied or moved from another container if the allocators differ, and how the allocators get copied or moved. 在复制和移动对象时,如果分配器不同,是否可以从另一个容器复制或移动一个容器,以及如何复制或移动分配器,则有特定的规则。

Just to answer your question first: No, you can definitely not assume that it makes sense to copy your allocator around, and your allocator may not even be copyable. 只是首先回答你的问题:不,你绝对不能认为复制你的分配器是有意义的,你的分配器甚至可能不是可复制的。

Here is 23.2.1/7 on this subject: 关于这个问题,这是23.2.1 / 7:

Unless otherwise specified, all containers defined in this clause obtain memory using an allocator (see 17.6.3.5). 除非另有说明,否则本节中定义的所有容器都使用分配器获取内存(见17.6.3.5)。 Copy constructors for these container types obtain an allocator by calling allocator_traits<allocator_-type>::select_on_container_copy_construction on their first parameters. 这些容器类型的复制构造函数通过在其第一个参数上调用allocator_traits<allocator_-type>::select_on_container_copy_construction来获取分配器。 Move constructors obtain an allocator by move construction from the allocator belonging to the container being moved. 移动构造函数通过从属于正在移动的容器的分配器移动构造来获取分配器。 Such move construction of the allocator shall not exit via an exception. 分配器的这种移动构造不应通过例外退出。 All other constructors for these container types take an Allocator& argument (17.6.3.5), an allocator whose value type is the same as the container's value type. 这些容器类型的所有其他构造函数都使用Allocator& argument(17.6.3.5),这是一个分配器,其值类型与容器的值类型相同。 [Note: If an invocation of a constructor uses the default value of an optional allocator argument, then the Allocator type must support value initialization. [注意:如果构造函数的调用使用可选allocator参数的默认值,则Allocator类型必须支持值初始化。 —end note] A copy of this allocator is used for any memory allocation performed, by these constructors and by all member functions, during the lifetime of each container object or until the allocator is replaced. -end note]此分配器的副本用于由这些构造函数和所有成员函数执行的任何内存分配,在每个容器对象的生存期内或直到替换分配器。 The allocator may be replaced only via assignment or swap(). 分配器只能通过赋值或swap()替换。 Allocator replacement is performed by copy assignment, move assignment, or swapping of the allocator only if allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value , allocator_traits<allocator_type>::propagate_on_container_move_assignment::value , or allocator_traits<allocator_type>::propagate_on_container_swap::value is true within the implementation of the corresponding container operation. 仅当allocator_traits<allocator_type>::propagate_on_container_copy_assignment::valueallocator_traits<allocator_type>::propagate_on_container_move_assignment::valueallocator_traits<allocator_type>::propagate_on_container_swap::value ,才能通过复制分配,移动分配或交换分配器来执行分配器替换。 allocator_traits<allocator_type>::propagate_on_container_swap::value在相应的容器操作的实现中是真的。 The behavior of a call to a container's swap function is undefined unless the objects being swapped have allocators that compare equal or allocator_traits<allocator_type>::propagate_on_container_swap::value is true. 除非被交换的对象具有比较相等的allocator_traits<allocator_type>::propagate_on_container_swap::value或者allocator_traits<allocator_type>::propagate_on_container_swap::value为true,否则调用容器的交换函数的行为是未定义的。 In all container types defined in this Clause, the member get_allocator() returns a copy of the allocator used to construct the container or, if that allocator has been replaced, a copy of the most recent replacement. 在本条款中定义的所有容器类型中,成员get_allocator()返回用于构造容器的分配器的副本,或者,如果已替换该分配器,则返回最近替换的副本。

See also the documentation of std::allocator_traits for a synopsis. 另请参阅std::allocator_traits的文档以获取概要。

In C++11 section 17.6.3.5 Allocator requirements [allocator.requirements] specifies the requirements for conforming allocators. 在C ++ 11第17.6.3.5节中,分配器要求[allocator.requirements]指定了符合分配器的要求。 Among the requirements are: 其中包括:

X                    an Allocator class for type T
...
a, a1, a2            values of type X&
...
a1 == a2             bool          returns true only if storage
                                   allocated from each can be
                                   deallocated via the other.
                                   operator== shall be reflexive,
                                   symmetric, and transitive, and
                                   shall not exit via an exception.
...
X a1(a);                           Shall not exit via an exception.
                                   post: a1 == a

Ie when you copy an allocator, the two copies are required to be able to delete each other's pointers. 即,当您复制分配器时,两个副本需要能够删除彼此的指针。

Conceivably one could put internal buffers into allocators, but copies would have to keep a list of other's buffers. 可以想象,可以将内部缓冲区放入分配器中,但是副本必须保留其他缓冲区的列表。 Or perhaps an allocator could have an invariant that deallocation is always a no-op because the pointer always comes from an internal buffer (either from your own, or from some other copy). 或者,分配器可能具有不变量,即释放总是无操作,因为指针总是来自内部缓冲区(来自您自己的缓冲区或来自其他副本)。

But whatever the scheme, copies must be "cross-compatible". 但无论方案如何,副本都必须是“交叉兼容”的。

Update 更新

Here is a C++11 conforming allocator that does the "short string optimization". 这是符合C ++ 11标准的分配器,它执行“短字符串优化”。 To make it C++11 conforming, I had to put the "internal" buffer external to the allocator so that copies are equal: 为了使C ++ 11符合要求,我必须将“内部”缓冲区放在分配器外部,以便副本相等:

#include <cstddef>

template <std::size_t N>
class arena
{
    static const std::size_t alignment = 16;
    alignas(alignment) char buf_[N];
    char* ptr_;

    std::size_t 
    align_up(std::size_t n) {return n + (alignment-1) & ~(alignment-1);}

public:
    arena() : ptr_(buf_) {}
    arena(const arena&) = delete;
    arena& operator=(const arena&) = delete;

    char* allocate(std::size_t n)
    {
        n = align_up(n);
        if (buf_ + N - ptr_ >= n)
        {
            char* r = ptr_;
            ptr_ += n;
            return r;
        }
        return static_cast<char*>(::operator new(n));
    }
    void deallocate(char* p, std::size_t n)
    {
        n = align_up(n);
        if (buf_ <= p && p < buf_ + N)
        {
            if (p + n == ptr_)
                ptr_ = p;
        }
        else
            ::operator delete(p);
    }
};

template <class T, std::size_t N>
class stack_allocator
{
    arena<N>& a_;
public:
    typedef T value_type;

public:
    template <class U> struct rebind {typedef stack_allocator<U, N> other;};

    explicit stack_allocator(arena<N>& a) : a_(a) {}
    template <class U>
        stack_allocator(const stack_allocator<U, N>& a)
            : a_(a.a_) {}
    stack_allocator(const stack_allocator&) = default;
    stack_allocator& operator=(const stack_allocator&) = delete;

    T* allocate(std::size_t n)
    {
        return reinterpret_cast<T*>(a_.allocate(n*sizeof(T)));
    }
    void deallocate(T* p, std::size_t n)
    {
        a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T));
    }

    template <class T1, std::size_t N1, class U, std::size_t M>
    friend
    bool
    operator==(const stack_allocator<T1, N1>& x, const stack_allocator<U, M>& y);

    template <class U, std::size_t M> friend class stack_allocator;
};

template <class T, std::size_t N, class U, std::size_t M>
bool
operator==(const stack_allocator<T, N>& x, const stack_allocator<U, M>& y)
{
    return N == M && &x.a_ == &y.a_;
}

template <class T, std::size_t N, class U, std::size_t M>
bool
operator!=(const stack_allocator<T, N>& x, const stack_allocator<U, M>& y)
{
    return !(x == y);
}

It could be used like this: 它可以像这样使用:

#include <vector>

template <class T, std::size_t N> using A = stack_allocator<T, N>;
template <class T, std::size_t N> using Vector = std::vector<T, stack_allocator<T, N>>;

int main()
{
    const std::size_t N = 1024;
    arena<N> a;
    Vector<int, N> v{A<int, N>(a)};
    v.reserve(100);
    for (int i = 0; i < 100; ++i)
        v.push_back(i);
    Vector<int, N> v2 = std::move(v);
    v = v2;
}

All allocations for the above problem are drawn from the local arena which is 1 Kb in size. 针对上述问题的所有分配都来自当地arena ,其大小为1 Kb。 You should be able to pass this allocator around by value or by reference. 您应该能够通过值或引用传递此分配器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如果我不服从无国籍的自定义分配器教条会发生什么? - worst that can happen if i don't obey the stateless custom allocators dogma? 为什么内存分配器没有主动将释放的内存返回给操作系统? - Why don't memory allocators actively return freed memory to the OS? 分配器:“没有可用的成员”。 我该如何解决? - Allocators: “No members available”. How can I fix that? 不能洗(void),所以free()是UB? - Can't launder(void), therefore free() is UB? 我可以使用stl分配器来创建一个完整的stl容器,它的内容(键和值)是否存储在我创建的内存块中? - Can I use stl allocators to make both an entire stl container and it's contents (keys and values) be stored inside a memory block that I created? C++ STL:为什么分配器不会增加容器的内存占用? - C++ STL: Why allocators don't increase memory footprint of containers? 内存分配器 - Memory allocators 是否可以将更快的方法或优化应用于临时内存池? - Is there a faster way or optimization I can apply to my improvised memory pool? 我可以将一个内存池用于多个向量吗? - Can I use one memory pool for multiple vectors? 我可以安全地假设将对if(true)和if(false)进行优化吗? - Can I safely assume that if(true) and if(false) will be optimized?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM