This code is what I want to do:
Tony& Movie::addTony()
{
Tony *newTony = new Tony;
std::unique_ptr tony(newTony);
attachActor(std::m
After move
, unique_ptr
s are set to nullptr
. Finally, I think it depends on what attachActor
is doing, however, in many cases, a good approach would be to use move semantics to guarantee single ownership for Tony at all times which is a way to reduce the risks of some types of bugs. My idea is to try to mimic ownership and borrowing from Rust.
#include <string>
#include <memory>
#include <vector>
#include <iostream>
using namespace std;
class Tony {
public:
string GetFullName(){
return "Tony " + last_name_;
}
void SetLastName(string lastname) {
last_name_ = lastname;
}
private:
string last_name_;
};
class Movie {
public:
unique_ptr<Tony> MakeTony() {
auto tony_ptr = make_unique<Tony>();
auto attached_tony_ptr = AttachActor(move(tony_ptr));
return attached_tony_ptr;
}
vector<string> GetActorsList(){
return actors_list_;
}
private:
unique_ptr<Tony> AttachActor(unique_ptr<Tony> tony_ptr) {
tony_ptr->SetLastName("Garcia");
actors_list_.push_back(tony_ptr->GetFullName());
return tony_ptr; // Implicit move
}
vector<string> actors_list_;
};
int main (int argc, char** argv) {
auto movie = make_unique<Movie>();
auto tony = movie->MakeTony();
cout << "Newly added: " << tony->GetFullName() << endl;
for(const auto& actor_name: movie->GetActorsList()) {
cout << "Movie actors: " << actor_name << endl;
}
}
The standard says (§ 20.8.1.2.1 ¶ 16, emphasis added) that the move constructor of std::unique_ptr
unique_ptr(unique_ptr&& u) noexcept;
Constructs a
unique_ptr
by transferring ownership fromu
to*this
.
Therefore, after you move-construct the temporary object that gets passed as argument to attachActor
form your tony
, tony
no longer owns the object and hence tony.get() == nullptr
. (This is one of the few cases where the standard library actually makes assertions about the state of a moved-away-from object.)
However, the desire to return the reference can be fulfilled without resorting to naked new
and raw pointers.
Tony&
Movie::addTony()
{
auto tony = std::make_unique<Tony>();
auto p = tony.get();
attachActor(std::move(tony));
return *p;
}
This code assumes that attachActor
will not drop its argument on the floor. Otherwise, the pointer p
would dangle after attachActor
has return
ed. If this cannot be relied upon, you'll have to re-design your interface and use shared pointers instead.
std::shared_ptr<Tony>
Movie::addTony()
{
auto tony = std::make_shared<Tony>();
attachActor(tony);
return tony;
}
No, you cannot do that instead. Moving the unique_ptr
nulls it. If it didn't, then it would not be unique. I am of course assuming that attachActor
doesn't do something silly like this:
attachActor(std::unique_ptr<Tony>&&) {
// take the unique_ptr by r-value reference,
// and then don't move from it, leaving the
// original intact
}
Section 20.8.1 paragraph 4.
Additionally, u (the unique_ptr object) can, upon request, transfer ownership to another unique pointer u2. Upon completion of such a transfer, the following postconditions hold:
-- u2.p is equal to the pre-transfer u.p,
-- u.p is equal to nullptr, and
-- if the pre-transfer u.d maintained state, such state has been transferred to u2.d.