vector::emplace_back() fills in non-sense arguments when used with pmr & default ctor

134 Views Asked by At

The following code tries to emplace_back a default-initialized profile class object into a pmr vector. As vector is initialized with the allocator, it enriches the childs constructor with its own allocator argument. However, there's a second argument which is added and this is why I get a huge error:

Demo

#include <memory_resource>
#include <cstdio>

struct profile
{
    using allocator_type = std::pmr::polymorphic_allocator<std::byte>;

    profile(allocator_type allocator = {}) {}
};

struct update
{
    using allocator_type = std::pmr::polymorphic_allocator<std::byte>;

    update(allocator_type allocator = {})
        :   profiles_( allocator )
    {
        
    }

    std::pmr::vector<profile> profiles_;
};


int main() {
    update u;

    u.profiles_.emplace_back();
}

For full error message check out above demo. The key part seems to be this:

stl_construct.h: In substitution of 'template<class _Tp, class ... _Args> constexpr decltype (::new(void*(0)) _Tp) std::construct_at(_Tp*, _Args&& ...) [with _Tp = profile; _Args = {profile, const std::pmr::polymorphic_allocator<profile>&}]':

You can see that an argument of type profile is added additionally to the construct_at method, which is why the constructor of profile receives two arguments instead of just one (the allocator). Why is that and how can I prevent this from happening?

1

There are 1 best solutions below

0
user17732522 On

If your type advertises that it supports uses-allocator construction, then all of the constructors should support it.

In your case there are only implicit move and copy constructors which do not support it. The former is used by std::vector's modifiers, e.g. by emplace_back in case a reallocation and move of elements becomes necessary. The profile you are complaining about corresponds to the argument to such a move-insertion.

So add overloads

profile(profile&&, allocator_type) noexcept;
//or
profile(std::allocator_arg_t, allocator_type, profile&&) noexcept;

and

profile(const profile&, allocator_type);
//or
profile(std::allocator_arg_t, allocator_type, const profile&) noexcept;

or equivalent overloads. (Using the std::allocator_arg_t variant is probably a safer choice, so I would recommend you use it for your default constructors as well.)