Reputation: 1253
Background
I want to use boost::circular_buffer
with a scoped C++17 std::pmr::polymorphic_allocator
. I.e. I want the same allocator for an outer container to be used for inner containers.
Side note:
boost::circular_buffer
is allocator-aware and the following assertion is true:
static_assert(std::uses_allocator_v<
boost::circular_buffer<int, std::pmr::polymorphic_allocator<int>>,
std::pmr::polymorphic_allocator<int>>);
In this scenario I have a vector of circular buffers. With default allocator it would be of type std::vector<boost::circular_buffer<T>>
Using std::pmr::polymorphic_allocator
I express it as:
#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>;
Using these aliases, Inner
works with std::pmr::polymorphic_allocator
, but not as element of Outer
.
The following works:
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);
But when used as scoped allocator (which std::pmr::polymorphic_allocator
supports without any need for std::scoped_allocator_adapter
) it fails to compile with difficult to interpret errors.
One of the errors is a static_assertion
failure because circular_buffer
is not constructible using the provided allocator, which I'm reproducing (possibly incorrectly) but without triggering assertion here:
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
Compiler explorer link
https://godbolt.org/z/4n1Gjhqxh
Questions
std::pmr::polymorphic_allocator
incorrectly here?emplace_back
?std::pmr::polymorphic_allocator
introduce any new/additional allocator requirements that make it incompatible with boost::circular_buffer
or is this an issue with boost::circular_buffer
?Edit
A
in replication of STL template parameters.Upvotes: 2
Views: 187
Reputation: 137315
The scoped allocator protocol requires that every constructor of the type has a corresponding allocator-extended version (either by appending an allocator parameter to the parameter list, or by prepending two parameters - allocator_arg_t
followed by the allocator).
That includes the copy and move constructors, for which boost::circular_buffer
doesn't appear to provide allocator-extended versions. The requirement for these two allocator-extended constructors in particular is in the C++11 allocator-aware container requirements.
For vector
in particular, emplace_back
needs to be able to copy or move existing elements on a reallocation, which is why it needs the allocator-extended copy/move constructor.
Upvotes: 1
Reputation: 275405
The first error message states that this isn't valid:
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
);
it appears to be trying to copy the circular buffer while passing in an allocator as the 2nd argument.
No overload of circular_buffer
accepts these two arguments.
static_assert(std::is_constructible_v< Inner, Inner, Alloc<int>& >);
that is basically the assertion that is failing.
Upvotes: 0