Search code examples
c++movestdvectorunique-ptremplace

Creating vector of move-constructed objects


I have a class which contains a constructor which moves an object during its construction:

class SomeClass
{
private:
    const std::unique_ptr<Base> foo;

public:
    template <typename T>
    inline explicit SomeClass(T&& derived) noexcept
     : foo(std::make_unique<T>(derived))
    {
        static_assert(std::is_base_of<Base, T>::value);
    }
};

The an object of the class can be constructed without issue when I require only a single instance:

class Derived : public Base
{
    // ...
};

Derived bar(...);
SomeClass baz(std::move(bar));

// Or

SomeClass baz(Derived(...));

However I am unable to emplace (or push) any objects of type SomeClass to a std::vector<SomeClass>.

std::vector<SomeClass> vec;

Derived bar(...);
vec.emplace_back(std::move(bar)); // Does not work.

vec.emplace_back(Derived(...));   // Does not work.

Please could you explain why objects can not be emplaced? I thought that the perfect forwarding that emplace_back used would allow construction of an instance of SomeClass inplace in the same way that a single instance could be constructed.

Please could you also explain how things might be modified to allow construction of a std::vector<SomeClass>?

My guess is that as constructor arguments are passed by move, then they are not being forwarded all the way to the constructor within the emplace_back method.


Solution

  • std::vector::emplace_back imposes the following requirements on value_type:

    Type requirements

    -T (the container's element type) must meet the requirements of MoveInsertable and EmplaceConstructible.

    A const member of a class implicitly deletes move constructor, i.e. SomeClass is not MoveInsertable because of const std::unique_ptr<Base> foo.

    Solution: remove const from foo.

    struct Base {};
    struct Derived : public Base {};
    
    class SomeClass
    {
    private:
        std::unique_ptr<Base> foo;
    
    public:
        template <typename T>
        inline explicit SomeClass(T&& derived)
            noexcept(std::is_nothrow_constructible_v<decltype(foo), T&&>) // (1)
         : foo(std::make_unique<T>(std::forward<T>(derived))) // (2)
        {
            static_assert(std::is_base_of<Base, T>::value);
        }
    };
    
    int main()
    {
        std::vector<SomeClass> vec;
    
        Derived bar{};
        vec.emplace_back(std::move(bar));
    
        vec.emplace_back(Derived{}); 
    }
    

    Live example.

    As a side note, I would suggest to make noexcept conditional depending on std::is_nothrow_constructible (1) and pass std::forward<T>(derived) to std::make_unique to utilise forwarding reference (2).