如何实现分配器感知的容器分配

How is allocator-aware container assignment implemented?

本文关键字:感知 分配 分配器 何实现 实现      更新时间:2023-10-16

例如,来自 std :: Deque :: operator = in C 参考:
(1)复制分配(const std :: Deque& other)

用其他内容的副本代替了内容。
如果 std :: allocator_traits :: propagate_on_container_copy_assignment()是 没错,目标分配器被源的副本替换 分配器。如果目标和源分配器未比较 相等,目标(*此)分配器用于处理内存, 然后,其他分配器用于在复制之前分配 元素。

如果this->get_allocator() == other.get_allocator(),如果需要的话,我可以简单地销毁和交易 this'元素,或在需要时分配和构造元素,或在需要时复制从 other*this的元素。
但是如果没有怎么办?上面的报价是否意味着我不能复制元素,因此我必须首先使用this->get_allocator()销毁和处理所有元素,然后使用other.get_allocator()
分配和构造元素但是,如果是这样,为什么我要使用other.get_allocator()进行分配?
稍后不会导致某些运行时错误,因为this无法正确处理内存?

(2)移动分配 (std :: Deque&&&&&&&&strong>

用其他内容代替内容 使用移动语义(即,另一个数据从其他数据移动到 这个容器)。其他人之后处于有效但未指定的状态。 如果 std :: allocator_traits :: propagate_on_container_move_assignment() 是的,目标分配器被源的副本替换 分配器。如果是错误的,并且源和目标分配器确实 不能比较平等,目标不能占有来源 内存,必须单独移动每个元素,分配 根据需要,使用自己的分配器进行其他内存。无论如何,所有人 *最初存在 *中存在的元素要么被破坏或更换 通过ElementWise Move-Assignment。

如果this->get_allocator() == other.get_allocator(),这是一件容易的任务。
但是,如果不是,则以上相同的问题如下,除非在这种情况下使用移动分配。

在这两种情况下,我都有一个其他问题。
如果这些元素既不能被复制分配或移动分配,可以将其销毁并与其他元素构造吗?如果是这样,我应该使用谁的分配器?

pocca(在container-copy-copy-appy-appy-Assignment)分配器被复制分配为容器的副本分配。同样,当容器的移动分配时,POCMA分配器也会移动。

上面的报价是否意味着我不能复制元素,因此我必须首先使用this->get_allocator()销毁和处理所有元素,然后使用other.get_allocator()分配和构造元素?

正确。

但是,如果是这样,为什么我应该使用other.get_allocator进行分配?稍后不会导致某些运行时错误,因为this->get_allocator()无法正确处理内存?

由于分配传播分配器:分配后,this->get_allocator()other.get_allocator()的副本,因此它可以安全地处理由其分配的内存。

如果this->get_allocator() == other.get_allocator(),这是一件容易的任务。但是,如果不是,则以上相同的问题如下,除非在这种情况下使用移动分配。

实际上,这是完全不同的。使用POCMA分配器移动分配是微不足道的:您可以销毁*this中的所有元素,释放内存,并掠夺other的内存和分配。

容器移动分配必须诉诸于元素移动分配/构建的唯一情况是,当您具有 non-pocma 分配器和分配器比较不平等时。在这种情况下,所有分配和构造都是用this->get_allocator()完成的,因为您没有传播任何东西。

在这两种情况下,我都有一个其他问题。如果这些元素既不能被复制分配或移动分配,可以将其销毁并与其他元素构造吗?如果是,我应该使用谁的分配?

使用最初构建的分配器将其销毁;使用分配器构建它将被破坏。换句话说,如果您要传播分配器,请用目标分配器将其销毁并用源分配器构造。

我正在回答自己的问题,以显示我得到了什么。-Dannyu ndos,2017年1月16日

在副本或移动分配中,其行为取决于两个条件:
1。分配器比较相等吗?(也就是说,源分配器能够破坏和处理目标容器的元素吗?)
2。在容器分配过程中,源的分配器的分配器是否传播(=分配给目标)?

用于副本分配:
A。如果分配器比较相等:
可以安全地完成将元素直接复制分配元素。
由于分配器已经比较平等,因此分配器是否传播都无关紧要。如果需要构造或破坏任何元素,那也无关紧要。
b。如果分配器不相等:
B.A。如果分配器不传播:
可以安全地完成直接复制元素到元素,但是如果需要构造或破坏任何元素,则源分配器必须这样做,因为只有它才能破坏目标容器的元素。

b.b。如果分配器传播:
首先,目标分配器必须贫穷并处理所有目标容器的元素。
然后分配器繁殖,然后源分配器分配和复制构造所有源容器的元素。

移动分配:
A。如果分配器比较相等:
目标容器删除其所有元素,然后占据源容器元素的所有权。这需要O(1)时间。
b。如果分配器不相等:
B.A。如果分配器不传播:
可以安全地完成将元素分配到元素上,但是如果需要构造或破坏任何元素,则源分配器必须这样做,因为只有它才能破坏源容器的元素。这需要O(n)时间。源容器必须在分配后处于有效状态。
b.b。如果分配器传播:
首先,目标分配器必须贫穷并处理所有目标容器的元素。
然后分配器繁殖,然后源分配器分配并移动构造所有源容器的元素。这需要O(n)时间。分配后,源容器必须处于有效状态。

在源代码中,给定alloc是容器的分配器,Alloc是它的类型,它们通常像这样写:

/*container*/ &operator = (const /*container*/ &other) {
    if (std::allocator_traits<Alloc>::propagate_on_container_copy_assignment::value && alloc != other.alloc) {
        clear();
        alloc = other.alloc;
        // directly copy-constructs the elements.
    } else {
        // directly copy-assigns the elements.
        // alloc does all allocation, construction, destruction, and deallocation as needed.
    }
    return *this;
}
/*container*/ &operator = (/*container*/ &&other) 
noexcept(std::allocator_traits<Alloc>::is_always_equal::value) {
    if (alloc == other.alloc) {
        clear();
        // *this takes ownership of other's elements.
    } else if (std::allocator_traits<Alloc>::propagate_on_container_move_assignment::value) {
        clear();
        alloc = other.alloc;
        // directly move-constructs the elements.
    } else {
        // directly move-assigns the elements.
        // alloc does all allocation, construction, destruction, and deallocation as needed.
    }
    // the source container is made valid, if needed.
    return *this;
}