c++allocatoremplace

nested vector with polymorphic_allocator cannot be constructed via emplace_back


The following code:

std::pmr::vector<std::pmr::vector<int>> outer_vec(std::pmr::get_default_resource());
outer_vec.emplace_back(std::pmr::get_default_resource());

fails with quite inconceivable error message ending with

static assertion failed: construction with an allocator must be possible if uses_allocator is true

The vector can clearly be constructed with an argument of std::pmr::get_default_resource(), such as:

std::pmr::vector<std::pmr::vector<int>> outer_vec(std::pmr::get_default_resource());
std::pmr::vector<int> inner_vec(std::pmr::get_default_resource());
outer_vec.push_back(std::move(inner_vec));

And std::pmr::vector can use emplace_back to construct an element, such as:

std::pmr::vector<std::vector<int>> outer_vec(std::pmr::get_default_resource());
outer_vec.emplace_back();

Why does the combination of both fails? What static_assert is failing?

Note that if the outer vector is not pmr, it works:

std::vector<std::pmr::vector<int>> vec;

vec.emplace_back(std::pmr::get_default_resource());

Solution

  • std::pmr::polymorphic_allocator uses uses-allocator construction if supported by the constructed type. std::vector does support uses-allocator construction and the allocator type of the inner std::vector is compatible with that of the outer one.

    In consequence uses-allocator construction will be used by the outer vector. This means, whenever the vector constructs a new element it will automatically pass its own allocator as another argument to the constructor of the element. (Specifically get_allocator()->resource() is passed at the end of the argument list in this case.)

    You are trying to pass an allocator manually to emplace_back, so that in effect with the above two allocators will be passed to the inner vector's construction, resulting in failure.

    So, what you really want is just outer_vec.emplace_back();. It will propagate the outer vector's allocator to the inner vector.

    Note that outer_vec.push_back(std::move(inner_vec)); will do the same. It will also propagate the outer allocator to the new "move"-constructed inner vector, resulting in moving all elements to a new allocation if necessary. It won't use the original allocation of inner_vec if its allocator compares unequal to outer_vec's allocator.