What happens to unique_ptr after std::move()?

后端 未结 3 1774
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-02-03 19:43

This code is what I want to do:

Tony& Movie::addTony()
{
    Tony *newTony = new Tony;
    std::unique_ptr tony(newTony);
    attachActor(std::m         


        
相关标签:
3条回答
  • 2021-02-03 19:58

    After move, unique_ptrs 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;
            }
        }
    
    0 讨论(0)
  • 2021-02-03 20:02

    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 from u 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 returned. 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;
    }
    
    0 讨论(0)
  • 2021-02-03 20:07

    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.

    0 讨论(0)
提交回复
热议问题