【问题标题】:Using scoped `polymorphic_allocator` with `boost::circular_buffer` fails使用作用域的 `polymorphic_allocator` 和 `boost::circular_buffer` 失败
【发布时间】:2021-05-22 12:58:39
【问题描述】:

背景

我想将 boost::circular_buffer 与作用域 C++17 std::pmr::polymorphic_allocator 一起使用。 IE。我希望将外部容器的相同分配器用于内部容器。

旁注

boost::circular_bufferis allocator-aware 并且以下断言为真:

static_assert(std::uses_allocator_v<
                boost::circular_buffer<int, std::pmr::polymorphic_allocator<int>>,
                std::pmr::polymorphic_allocator<int>>);

在这种情况下,我有一个循环缓冲区向量。使用默认分配器,它的类型为std::vector&lt;boost::circular_buffer&lt;T&gt;&gt;

使用std::pmr::polymorphic_allocator我表示为:

#include <vector>
#include <memory_resource>
#include <boost/circular_buffer.hpp>

template<class T>
using Alloc = std::pmr::polymorphic_allocator<T>;
using Inner = boost::circular_buffer<int, Alloc<int>>;
using Outer = std::pmr::vector<Inner>;

使用这些别名,Inner 可与 std::pmr::polymorphic_allocator 一起使用,但不能作为 Outer 的元素。

以下作品:

Outer::allocator_type alloc1; // Allocator type used by Outer to allocate Inner
Inner::allocator_type alloc2; // Allocator type used by Inner to allocate ints
// circular_buffer works if used directly with polymorphic_allocator
Inner inner1; // default arg OK
Inner inner2(alloc1); // pmr with allocator as last argument, also implicitly converts OK
Inner inner3(1, alloc2); // pmr with allocator as last argument OK

// Use to instantiate member functions
inner1.set_capacity(16);
inner2.set_capacity(16);
inner3.set_capacity(16);
inner1.push_back(1);
inner2.push_back(1);
inner3.push_back(1);

但是当用作作用域分配器(std::pmr::polymorphic_allocator 支持without any need for std::scoped_allocator_adapter)时,它无法使用difficult to interpret errors 进行编译。

其中一个错误是 static_assertion 失败,因为 circular_buffer 无法使用提供的分配器构造,我正在重现(可能不正确)但没有在此处触发断言:

Outer v;
/* Statically asserts because Inner is not constructible

c++/12.0.0/bits/uses_allocator.h:98:60:error: static assertion failed: construction with an allocator must be possible if uses_allocator is true
98 |           is_constructible<_Tp, _Args..., const _Alloc&>>::value,

[with
_Args = {};
_Tp = boost::circular_buffer<int, std::pmr::polymorphic_allocator<int> >;
_Alloc = std::pmr::polymorphic_allocator<boost::circular_buffer<int, std::pmr::polymorphic_allocator<int> > >;
std::vector<_Tp, _Alloc>::reference = boost::circular_buffer<int, std::pmr::polymorphic_allocator<int> >&]
*/

// Note: Adding prefix `A` to make names allowed
using A_Tp = boost::circular_buffer<int, std::pmr::polymorphic_allocator<int> >;
using A_Alloc = std::pmr::polymorphic_allocator<boost::circular_buffer<int, std::pmr::polymorphic_allocator<int> > >;
static_assert(std::is_constructible<Inner, const A_Alloc&>::value); // OK
static_assert(std::is_same_v<Inner, A_Tp>); // OK
static_assert(std::is_same_v<typename Outer::allocator_type, A_Alloc>); // OK
v.emplace_back(); // ERROR
v.emplace_back(1); // ERROR 

编译器浏览器链接

https://godbolt.org/z/4n1Gjhqxh

问题

  1. 我在这里错误地使用了std::pmr::polymorphic_allocator 吗?
  2. 编译器试图通过错误告诉我什么?
  3. 为什么在 emplace_back 中使用时重现的断言没有失败?
  4. std::pmr::polymorphic_allocator 是否引入了任何新的/额外的分配器要求,使其与 boost::circular_buffer 不兼容,或者这是 boost::circular_buffer 的问题?

编辑

  • 在 STL 模板参数的复制中添加了前缀 A

【问题讨论】:

  • 好吧,_Alloc 使您的程序格式错误,无需诊断。修复它不会解决您的问题。 (停止模仿 std 头文件,它使用错位名称,因为这些错位名称保留供其使用)。没有_,后跟您的代码中的大写字母,或者__
  • 我保留了相同的名称,以使我试图展示的内容更明显,因为它对结果没有任何影响。尽管如此,我还是纠正了它。

标签: c++ c++17 c++pmr boost-circularbuffer


【解决方案1】:

范围分配器协议要求该类型的每个构造函数都有一个对应的分配器扩展版本(通过将分配器参数附加到参数列表,或者通过前置两个参数 - allocator_arg_t 后跟由分配器)。

这包括复制和移动构造函数,boost::circular_buffer 似乎没有提供分配器扩展版本。这两个分配器扩展构造函数的要求尤其是在 C++11 分配器感知容器要求中。

特别是对于vectoremplace_back 需要能够在重新分配时复制或移动现有元素,这就是它需要分配器扩展的复制/移动构造函数的原因。

【讨论】:

  • 解释一下我自己的理解:“必须有一个有效的构造函数重载,根据'Uses-allocator construction'接受分配器”。未满足的emplace_back 要求似乎是MoveInsertable
【解决方案2】:

第一条错误消息表明这是无效的:

std::pmr::polymorphic_allocator<boost::circular_buffer<int, std::pmr::polymorphic_allocator<int> > > alice;

boost::circular_buffer<int, std::pmr::polymorphic_allocator<int> > bob(
  std::declval<boost::circular_buffer<int, std::pmr::polymorphic_allocator<int> >>(),
  alice
);

它似乎试图复制循环缓冲区,同时将分配器作为第二个参数传递。

circular_buffer 没有重载接受这两个参数。

static_assert(std::is_constructible_v< Inner, Inner, Alloc<int>& >);

这基本上是失败的断言。

【讨论】:

  • 这不是第二个错误吗? emplace_back 的第一个错误是 with _Args = {},因此我不会尝试执行任何复制构造。鉴于此,是否可以相信后续错误不会产生误导?特别是因为它似乎执行"Uses-allocator construction"?
  • 我使用了 4n1 Godbolt,并解码了我看到的第一条错误消息。在您看来,该错误消息无法使用不同的分配器(pmr 到缓冲区,而不是 pmr 到 int)来构造它。
猜你喜欢
  • 2015-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-29
  • 1970-01-01
  • 2020-08-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多