I am having trouble understanding why the following copy-initialization doesn't compile:
#include <memory> struct base{}; struct derived : base{}; struct test { test(std::unique_ptr<base>){} }; int main() { auto pd = std::make_unique<derived>(); //test t(std::move(pd)); // this works; test t = std::move(pd); // this doesn't }
A unique_ptr<derived>
can be moved into a unique_ptr<base>
, so why does the second statement work but the last does not? Are non-explicit constructors not considered when performing a copy-initialization?
The error from gcc-8.2.0 is:
conversion from 'std::remove_reference<std::unique_ptr<derived, std::default_delete<derived> >&>::type' {aka 'std::unique_ptr<derived, std::default_delete<derived> >'} to non-scalar type 'test' requested
and from clang-7.0.0 is
candidate constructor not viable: no known conversion from 'unique_ptr<derived, default_delete<derived>>' to 'unique_ptr<base, default_delete<base>>' for 1st argument
Live code is available here.
A std::unique_ptr<base>
is not the same type as a std::unique_ptr<derived>
. When you do
test t(std::move(pd));
You call std::unique_ptr<base>
's conversion constructor to convert pd
into a std::unique_ptr<base>
. This is fine as you are allowed a single user defined conversion.
In
test t = std::move(pd);
You are doing copy initialization so so you need to convert pd
into a test
. That requires 2 user defined conversions though and you can't do that. You first have to convert pd
to a std::unique_ptr<base>
and then you need to convert it to a test
. It's not very intuitive but when you have
type name = something;
whatever something
is needs to be only a single user defined conversion from the source type. In your case that means you need
test t = test{std::move(pd)};
which only uses a single implicit user defined like the first case does.
Lets remove the std::unique_ptr
and look at in a general case. Since std::unique_ptr<base>
is not the same type as a std::unique_ptr<derived>
we essentially have
struct bar {}; struct foo { foo(bar) {} }; struct test { test(foo){} }; int main() { test t = bar{}; }
and we get the same error because we need to go from bar -> foo -> test
and that has one user defined conversion too many.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With