C++ allocators_trait::construct:动机,忽略的权衡

C++ allocators_trait::construct: motivation, trade-offs in ignoring

我正在与 std::allocator_traits::construct 引起的一些疼痛作斗争。为了使容器成为分配器概念的 "conforming" 用户,它需要使用 construct 而不是 placement new 来构造对象。这对我来说非常粘。目前我有一个 class (class A) 被设计为分配器感知,并且在某些时候它需要创建另一个 class (class B 的实例) 在分配的内存中。问题是 class B 实现了新对象的构造。如果我可以使用 placement new,这将不是问题:A 将处理分配,将内存地址传递给 B,然后 B 将构造到其中。但是由于构建需要通过构造来执行,我需要将分配器类型注入到 B 中,对其进行模板化,这造成了巨大的混乱。

我正在认真考虑只使用 placement new 和 static 断言我的分配器实例没有构造方法,这已经够糟糕了(请注意,静态构造函数如果存在则调用实例方法,否则它调用放置新)。我从未有过为分配器编写构造方法的丝毫冲动。使分配器概念的这一部分的成本对我来说似乎非常高;构造与分配纠缠在一起,分配器应该帮助将它们分开。 construct/destruct 存在的理由是什么?深入了解设计决策、真实(非玩具)用例的示例,或对选择简单地使用 placement new 的重要性的想法。

有一个类似的问题; std::allocator construct/destroy vs. placement new/p->~T()。很久以前有人问过这个问题,我觉得那里接受的答案还不够。日志记录作为一个用例有点老套,即便如此:为什么分配器要记录对象的实际构造?它可以在分配和解除分配中记录分配和解除分配,它没有回答这个问题:为什么首先将构造作为分配器的一个省?我希望找到更好的答案;已经好几年了,从那时起,分配器发生了很多变化(例如,分配器从 11 开始就是有状态的)。

几点:

  • 确实没有标准容器概念。标准中的容器要求表用于记录标准指定的容器。

  • 如果您有一个容器想要与 std::allocator_traits<Alloc> 交互,您所要做的就是假设 Alloc 符合最低 C++11 分配器要求并且通过 std::allocator_traits<Alloc>.

  • 与其互动
  • 不需要打电话给std::allocator_traits<Alloc>::construct

  • 禁止调用Alloc::construct,因为它可能不存在。

  • standard-specified容器只需要为container::value_type调用std::allocator_traits<Alloc>::construct,禁止在任何其他类型的容器上使用std::allocator_traits<Alloc>::construct可能需要构建(例如内部节点)。

为什么 construct 包含在 C++98 的 "allocator concept" 方式中?

可能是因为当时的委员会认为这会简化处理 x86 近指针和远指针的问题——这个问题如今已不复存在。

话虽这么说,std::scoped_allocator_adaptor 是现代 real-world 分配器示例,可自定义 constructdestroy。对于这些自定义的详细规范,我向您指出了最新的 C++1z 工作草案 N4567。该规范并不简单,这就是我不尝试在此处复制它的原因。