为什么自定义分配器的要求是可复制的?

时间:2014-12-13 18:11:28

标签: c++ c++11

C ++ 11标准(或至少this working draft)要求实现Allocator概念的类提供复制构造函数。

当所有分配器都必须是无状态时,这有意义,但是当在分配器类中允许状态时,提供复制构造函数可能并不可取。例如,如果分配器实现了某种slab分配系统或内存池,那么它将维护一个内部freelist。在我看来,这样的分配器应始终移动,从不复制。实际上复制它需要分配新的存储,然后完全重现原始分配器的内部状态,包括所有空闲内存块,空闲列表等。(或者“复制”Allocator只是意味着返回{{1 ,即返回一个新的默认构造的分配器?)

实际上复制一个有状态的分配器似乎是完全没必要的,因为据我所知,实际上没有真正的通用用例来复制构造一个分配器。否Allocator()实际上复制了一个分配器--C ++ 11容器将在容器拷贝构造函数中调用Container,而这通常只返回Allocator::select_on_container_copy_construction。然后容器通常只是从另一个容器中逐个元素地复制,可能只需要调用Allocator()

当然,优化的容器实现可能会使用有关容器结构的内部知识来执行更复杂的操作,但仍然没有人会复制构造Container::insert - Container将只调用other.get_allocator()来获取default-construct allocator。

那么为什么我们要求Allocator 本身必须是可复制构造的?不应该移动施工吗?


注意:要明确,此问题why does allocator in c++ need a copy constructor?的副本。那个问题是专门询问other.get_allocator().select_on_container_copy_construction()(无状态),而我问的是实现std::allocator概念的自定义分配器。

2 个答案:

答案 0 :(得分:0)

重申索赔

  

Container实际上复制了分配器

可能是这样(我没有经过测试),std::string可能被视为非Container,但仍然:

#include <string>
#include <iostream>
#include <memory>       // std::allocator

template< class Type >
struct Alloc
    : std::allocator<Type>
{
    using Base = std::allocator<Type>;

    template< class Other > 
    struct rebind
    { 
        using other = Alloc<Other>;
    };

    Alloc() {}

    Alloc( Alloc<Type> const& other )
        : Base( other )
    { std::clog << "Alloc::<copy>\n"; }

    template<class Other> 
    Alloc( Alloc<Other> const& other )
        : Base( other )
    { std::clog << "Alloc::<generic-copy>\n"; }
};

auto main() -> int
{
    using namespace std;
    basic_string< char, char_traits<char>, Alloc<char> > a = "A", b = "B";
    a = b;
}

使用Visual C ++输出:

Alloc::<copy>
Alloc::<copy>

使用g ++,分配器复制操作的数量更高。

答案 1 :(得分:0)

标准库容器通常具有引用现有分配器对象的 const 的构造函数;例如vectorsince C++20

constexpr explicit vector( const Allocator& alloc ) noexcept;

移动构造不是合适的语义,因为没有资源可以从 alloc 中移除,因为它是 const。为此目的,复制构造是必要的,因为 alloc 的生命周期未指定且 alloc 不能用于状态修改分配。另见Why is allocator const in vector?

您可以拥有一个引用底层分配类的分配器,以避免复制内存管理数据,或在多个分配器之间共享它。但是,只要提供适当的复制构造函数或 select_on_container_copy_construction 成员函数(可以调用默认构造函数),就可以安全地在分配器中使用状态来提供每个容器的内存.

给定一个分配器 A

std::allocator_traits<A>::select_on_container_copy_construction

returns a copy of A 如果 A 不提供成员函数 select_on_container_copy_construction