不可复制的 STL 分配器

Non-Copyable STL Allocator

我想创建一个不可复制的分配器(在 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

根据 requirements for an Allocator type your allocator type needs to satisfy CopyConstructible 这意味着您不能删除您的副本:

A a1(a)
A a1 = a

Copy-constructs a1 such that a1 == a. Does not throw exceptions. (Note: every Allocator also satisfies CopyConstructible)

这是不可能的。来自 [container.requirements.general]/8

[...] All other constructors for these container types take a const allocator_­type& argument. [ Note: If an invocation of a constructor uses the default value of an optional allocator argument, then the allocator type must support value-initialization. — end note ] A copy of this allocator is used for any memory allocation and element construction performed, by these constructors and by all member functions, during the lifetime of each container object or until the allocator is replaced.

强调我的

因此您不能将仅移动分配器传递给任何采用分配器的容器构造函数。

你说:我想 [...] 防止用户意外分配内存。

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

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

有许多可能适合您需要的替代实现。但我不确切地知道你在找什么所以我建议下面一个可以适应的例子,遵循 allocator.requirements:

中的要求
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++,您可以声明危险的构造函数私有:

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:/*...*/
   };