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.
std::vector::emplace_back
imposes the following requirements on value_type
:
Type requirements
-
T
(the container's element type) must meet the requirements ofMoveInsertable
andEmplaceConstructible
.
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{});
}
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).