I have a base class and its subclass:
class Base {
public:
virtual void hi() {
cout << "hi" << endl;
}
};
class Derived : public Base {
public:
void hi() override {
cout << "derived hi" << endl;
}
};
Trying to create a helper function that creates a unique pointer of a Derived object.
1) This one works:
std::unique_ptr<Base> GetDerived() {
return std::make_unique<Derived>();
}
2) But, this one fails to compile:
std::unique_ptr<Base> GetDerived2() {
auto a = std::make_unique<Derived>();
return a;
}
3) std::move works:
std::unique_ptr<Base> GetDerived3() {
auto a = std::make_unique<Derived>();
return std::move(a);
}
4) If I create a Base instance, both work:
std::unique_ptr<Base> GetDerived4() {
auto a = std::make_unique<Base>();
return a;
}
std::unique_ptr<Base> GetDerived5() {
auto a = std::make_unique<Base>();
return std::move(a);
}
Why (2) fails but others work?
std::unique_ptr
is not copyable, only movable. The reason you can return std::make_unique<Derived>
from a function declared to return std::unique_ptr<Base>
is that there is a conversion from one to the other.
So 1) is equivalent to:
std::unique_ptr<Base> GetDerived() {
return std::unique_ptr<Base>(std::made_unique<Derived>());
}
Since the value returned from std::make_unique
is an rvalue, the return value is move-constructed.
Contrast that to 2), which is equivalent to:
std::unique_ptr<Base> GetDerived2() {
std::unique_ptr<Derived> a = std::make_unique<Derived>();
return std::unique_ptr<Base>(a);
}
since a
is an lvalue, the return value must be copy-constructed, and std::unique_ptr
is non-copyable.
3) works because you cast the lvalue a
to an rvalue, and the return value can be move-constructed.
4) and 5) work because you already have a std::unique_ptr<Base>
and don't need to construct one to return.