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

Why the requirement for custom allocators to be copyconstructible?

本文关键字:可复制 分配器 自定义 为什么      更新时间:2023-10-16

C++11标准(或至少是这个工作草案(要求实现Allocator概念的类提供复制构造函数。

当所有分配器都必须是无状态的时,

这是有意义的,但是当分配器类中允许状态时,提供复制构造函数可能不是可取的。 例如,如果分配器实现了某种板分配系统或内存池,则在其中维护内部自由列表。 在我看来,这样的分配器应该始终移动,而不是复制。 实际上复制它需要分配新的存储,然后完全复制原始分配器的内部状态,包括所有可用内存块、自由列表等。 (或者"复制"一个Allocator只是意味着返回Allocator(),即返回一个新的默认构造的分配器?

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

当然,优化的容器实现可能会使用有关容器结构的内部知识做一些更复杂的事情,但仍然没有人会复制构造other.get_allocator() - 容器只会调用other.get_allocator().select_on_container_copy_construction()来获取默认构造的分配器。

那么,为什么我们要求分配器本身必须是可复制的呢? 搬家建设不应该足够吗?


注意:需要明确的是,这个问题不是为什么 c++ 中的分配器需要复制构造函数的重复。 这个问题专门询问std::allocator(无状态(,而我询问的是实现Allocator概念的自定义分配器。

重新声明

"> 没有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;
}

带可视C++的输出:

分配::<副本>分配::<副本>

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

标准库 容器通常具有构造函数,这些构造函数采用对现有分配器对象的引用const;例如 自 C++20 年以来vector

constexpr explicit vector( const Allocator& alloc ) noexcept;

移动构造不是合适的语义,因为不能从alloc中删除任何资源,因为它是const。复制构造对于此目的是必需的,因为alloc的生存期未指定,并且alloc不能用于状态修改分配。参见 为什么分配器常量在向量中?。

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

给定一个分配器A

std::allocator_traits<A>::select_on_container_copy_construction

如果A不提供成员函数select_on_container_copy_construction则返回A的副本。