为什么在 c++17 中弃用了 std::allocator 的构造函数和销毁函数?

Why are are std::allocator's construct and destroy functions deprecated in c++17?

c++17 规范弃用了 std::allocator object 的 constructdestroy 成员。工作组在 "Deprecate the redundant members of std::allocator".

标题下提供了弃用其他成员函数 here 的理由

但是,他们没有具体说明为什么不推荐使用这两个成员,也没有具体说明替换该功能的建议。我假设这意味着使用 std::allocator_traits::construct 代替。

由于 this comment about std::allocator_traits::construct

,我对在某些情况下是否仍然需要实施 construct 感到有点困惑

Because this function provides the automatic fall back to placement new, the member function construct() is an optional Allocator requirement since C++11.

对于自定义分配器(例如使用 memalign 的 page-aligned 内存),回退到放置 new 是否总能产生正确的行为?

这些函数与其他函数一起从论文中删除 D0174R0 Deprecating Vestigial Library Parts in C++17。如果我们查看相关部分,我们有

Many members of std::allocator redundantly duplicate behavior that is otherwise produced by std::allocator_traits<allocator<T>>, and could safely be removed to simplify this class. Further, addressof as a free function supersedes std::allocator<T>::address which requires an allocator object of the right type. Finally, the reference type aliases were initially provided as an expected means for extension with other allocators, but turned out to not serve a useful purpose when we specified the allocator requirements (17.6.3.5 [allocator.requirements]).

While we cannot remove these members without breaking backwards compatibility with code that explicitly used this allocator type, we should not be recommending their continued use. If a type wants to support generic allocators, it should access the allocator's functionality through allocator_traits rather than directly accessing the allocator's members - otherwise it will not properly support allocators that rely on the traits to synthesize the default behaviors. Similarly, if a user does not intend to support generic allocators, then it is much simpler to directly invoke new, delete, and assume the other properties of std::allocator such as pointer-types directly.

强调我的

所以合理的是我们不需要复制分配器中的所有代码,因为我们具有分配器特征。如果我们查看 std::allocator_traits,我们会发现它确实有

allocate
deallocate
construct
destroy
max_size

静态函数,因此我们可以使用它们而不是分配器中的那些。

allocator requirements table 表示 construct(c, args),如果提供,必须 "construct an object of type C at c"。

它完全没有说明 1) 将哪些参数传递给 C 的构造函数或 2) 如何传递这些参数。这是分配器的选择,事实上,标准中的两个分配器在将参数传递给 C 的构造函数之前会弄乱参数:std::scoped_allocator_adaptor and std::pmr::polymorphic_allocator。特别是在构建 std::pair 时,它们传递给 pair 的构造函数的参数甚至可能与它们收到的参数不同。

也没有要求完美转发;如果效率低下,则 C++03 风格 construct(T*, const T&) 符合规范。

std::allocatorconstructdestroy 已被弃用,因为它们毫无用处:没有好的 C++11 和更高版本的代码应该直接调用它们,并且它们没有添加任何内容默认值。


处理内存对齐应该是 allocate 的任务,而不是 construct