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