Reputation: 682
I have the following code and I'm trying to understand why the unique_ptr base
variable remains valid after calling foo()
. However, I haven't found an answer so far.
#include <memory>
#include <iostream>
struct Base
{
};
struct Derived : public Base
{
};
void foo(std::unique_ptr<Base>&& b)
{
}
int main()
{
std::unique_ptr<Derived> derived = std::make_unique<Derived>();
foo(std::move(derived));
std::cout << "derived after foo(): " << (derived? "valid\n" : "invalid\n");
std::unique_ptr<Base> base = std::make_unique<Derived>();
foo(std::move(base));
std::cout << "base after foo(): " << (base? "valid\n" : "invalid\n");
return 0;
}
which gives the output:
derived after foo(): invalid
base after foo(): valid
Could someone please explain why base
is still valid after calling foo()
while derived
becomes invalid?
Upvotes: 8
Views: 221
Reputation: 180385
First, std::move()
doesn't move anything, it just casts the object into an rvalue reference so it can be passed to a function that take an rvalue reference.
Second, this code also doesn't move anything:
void foo(std::unique_ptr<Base>&& b)
{
}
b
is an rvalue reference, and you never move it into anything, so after calling foo
the passed in object is not changed.
Now, why does foo(std::move(derived));
actually move derived
? That happens because std::unique_ptr<Base>
and std::unique_ptr<Derived>
are distinct types. This means when you pass std::move(derived)
then that is used to initialize a temporary std::unique_ptr<Base>
, and it is that temporary that gets passed to foo()
. A consequence of that is derived
gets moved into that temporary object.
That is why base
and derived
behave differently.
Upvotes: 17
Reputation: 392763
If you intend to pass ownership, the best practice is usually to pass by value:
void foo(std::unique_ptr<Base>) {}
Otherwise you end up just passing by reference if the type matched 1:1, and moving if you happened to cause an implicit conversion. Keep in mind rvalue-references are still just references.
#include <iostream>
#include <memory>
struct Base {};
struct Derived : public Base {};
void foo(std::unique_ptr<Base>) {}
int main() {
std::unique_ptr<Derived> derived = std::make_unique<Derived>();
foo(std::move(derived));
std::cout << "derived after foo(): " << (derived ? "valid\n" : "invalid\n");
std::unique_ptr<Base> base = std::make_unique<Derived>();
foo(std::move(base));
std::cout << "base after foo(): " << (base ? "valid\n" : "invalid\n");
}
Printing
derived after foo(): invalid
base after foo(): invalid
Upvotes: 7