如何使用自定义分配器无UB提供的指向原始内存的指针?

问题描述

我正在尝试编写一个可识别分配器的容器。假设我想为三个对象分配一块内存:

T* chunk = std::allocator_traits<Allocator>::allocate(allocator,3);

(我知道分配器可以具有自定义的指针类型,因此我应该使用std::allocator_traits<Allocator>::pointer;为简单起见,我在这里使用原始指针。)

现在,我想在索引2上创建一个实际的对象。我该怎么做?特别是,如何计算指向尚未存在的元素的指针?最明显的选择如下:

std::allocator_traits<Allocator>::construct(allocator,chunk + 2,...);

不幸的是,chunk + 2似乎不正确:according to the standard,指针运算只能在指向数组元素的指针上执行,否则会导致未定义的行为。出于同样的原因,我无法将指针转换为std::byte*并对其使用指针算术。 (虽然std::allocator被定义为在新分配的内存中创建数组,但是直到C ++ 20时,对自定义分配器都没有相同的要求。而且,C ++ 20为“隐式创建对象”添加了一些语言。 ”,这不适用于早期的C ++版本。)

那么如何在不引起未定义行为的情况下(在C ++ 20之前)计算要作为construct的第二个参数给出的指针?

解决方法

在最新的标准草案(C ++ 20)中:

[tab:cpp17.allocator]

a.allocate(n)-为n个T的数组分配内存,并且创建了这样的对象,但未构造数组元素。

allocator_traits::allocate(n)只需调用a.allocate(n)

因此,鉴于已创建数组,指针算法已得到很好的定义。


在接受提案P0593R6之前的C ++ 17中,措辞为:

已为创建的n个类型为T的对象分配了内存,但未构造对象。

在进行此更改之前,没有明确的方法可以执行您要问的事情,除非:

  • 我们假设自定义分配器可确保创建此类数组。问题在于,没有创建对象而不创建对象的标准方法(没有默认分配器),因此没有实现此类自定义分配器的标准方法。
  • 我们忽略了指针运算的限制。与此相关的理论问题是不确定的行为。实际上,这对于实际的语言实现来说不是问题。