不可复制STL分配器



我想创建一个不可复制的分配器(在C++14中(,它只分配std::vector可以使用的固定内存块。我想防止分配器(以及向量(是可复制的,以防止用户意外分配内存。分配器仅用于std::vectorstd::string

所以我的分配器有一个复制构造函数,如下所示:

static_allocator(const static_allocator<T>&) = delete;

呼叫时:

std::vector<int, static_allocator<int>> vvv(static_allocator<int>(3));

我得到以下编译错误:

/usr/include/c++/5/bits/stl_vector.h: In instantiation of ‘std::_Vector_base<_Tp, _Alloc>::_Vector_impl::_Vector_impl(const _Tp_alloc_type&) [with _Tp = int; _Alloc = static_allocator<int>; std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type = static_allocator<int>]’:
/usr/include/c++/5/bits/stl_vector.h:128:20:   required from ‘std::_Vector_base<_Tp, _Alloc>::_Vector_base(const allocator_type&) [with _Tp = int; _Alloc = static_allocator<int>; std::_Vector_base<_Tp, _Alloc>::allocator_type = static_allocator<int>]’
/usr/include/c++/5/bits/stl_vector.h:265:18:   required from ‘std::vector<_Tp, _Alloc>::vector(const allocator_type&) [with _Tp = int; _Alloc = static_allocator<int>; std::vector<_Tp, _Alloc>::allocator_type = static_allocator<int>]’

这个错误似乎是因为在stl_vector.h:265中没有定义右值分配器的构造函数:

/**
*  @brief  Creates a %vector with no elements.
*  @param  __a  An allocator object.
*/
explicit
vector(const allocator_type& __a) _GLIBCXX_NOEXCEPT
: _Base(__a) { }

虽然深层代码实际上支持右值分配器,但这些分配器没有被调用,因为右值是由上面提到的构造函数通过引用获得的。

这是C++14中缺少的功能,还是我缺少了一些选项?同样奇怪的是,分配器在构建向量时被复制,原因并不明显。

完整的代码示例可以在这里找到:https://onlinegdb.com/ByqXwQ4k4

根据Allocator类型的要求,您的分配器类型需要满足CopyConstructible,这意味着您不能删除您的副本con:

A a1(a)
A a1 = a

Copy构造CCD_ 7,使得CCD_。不抛出异常。(注:每个Allocator也满足CopyConstructible(

这是不可能的。来自【集装箱要求概述】/8

[…]这些容器类型的所有其他构造函数都采用const allocator_­type&参数。[注意:如果构造函数的调用使用可选分配器参数的默认值,那么分配器类型必须支持值初始化--尾注]此分配器的副本用于这些构造函数和所有成员函数在每个容器对象的生存期内执行的任何内存分配和元素构造,或者直到分配器被替换为止。

emphasis mine

因此,您不能将只移动分配器传递给任何接受分配器的容器构造函数。

您说过:我希望[…]防止用户意外分配内存

但是你提出的解决方案我想防止分配器(以及向量(像其他答案中所说的那样是可复制的是不可行的。正如上面写的那样,你的问题看起来像XY问题

其他人对您的尝试解决方案有答案。所以我只关注这个问题。因为可以编写一个符合标准的分配器来完全满足您的需求:以防止用户意外分配内存

有许多可供选择的实现方式可以满足您的需求。但我不知道你在寻找什么,所以我在下面提出了一个可以改编的例子,遵循分配器的要求。要求:

const size_t buffer_size = 4096;
unsigned char buffer[buffer_size];
void* _sbuffer = buffer; //or atomic

template<class T>
class allocator{
void* buffer = exchange(_sbuffer,nullptr);//could be done atomically
bool allocatable=buffer?true:false;
public:
using value_type = T;
T* allocate(size_t n){
if (n>buffer_size || !allocatable) throw std::bad_alloc{};
allocatable=false;
return static_cast<T*>(buffer);
}
void deallocate(T*,size_t){
if (buffer) allocatable=true;
}
//Here the intersting part:
allocator select_on_container_copy_construction(){
return allocator{};
}
allocator() =default;
//this copy constructor is only used internaly
//but will not be used to propagate the allocator
//from one container object to an other 
//(see select_on_container_copy_construction)
allocator(const allocator& other) =default;
allocator(allocator&& other)
:buffer{exchange(other.buffer,nullptr)}
,allocatable{exchange(other.allocatable,false)}
{}
allocator& operator=(const allocator&) =delete;
allocator& operator=(allocator&& other){
buffer=exchange(other.buffer,nullptr);
allocatable=exchange(other.allocatable,false);
return *this;
}
using propagate_on_container_copy_assignment = false_type;
using propagate_on_container_move_assignment = true_type;
using propagate_on_container_swap = true_type;

//any allocator can deallocate memory provided by an other
static constexpr bool is_always_equal = true;
friend bool operator==(const allocator&,const allocator&){
return true;
}
friend bool operator!=(const allocator&,const allocator&){
return false;
}
};

coliru 演示

它是脆弱的,因为如果分配器是在容器之外构造的,那么就构造副本,然后这些副本稍后用于初始化容器。。。您可以转到实现定义的行为,例如,对于libstdc++,您可以声明危险的构造函数private:

template<class T>
struct allocator{
/*...*/
friend std::_Vector_base<T,allocator>;
friend std::allocator_traits<allocator>;
private:
allocator() =default;
allocator(const allocator& other) =default;
public:/*...*/
};

最新更新