简体   繁体   中英

Move Semantics with unique_ptr

I am using Visual Studio 2012 Update 2 and am having trouble trying to understand why std::vector is trying to use the copy constructor of unique_ptr. I have looked at similar issues and most are related to not having an explicit move constructor and/or operator.

If I change the member variable to a string, I can verify that the move constructor is called; however, trying to use the unique_ptr results in the compilation error:

error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' .

I'm hoping someone can point me to what I am missing, thanks!

#include <vector>
#include <string>
#include <memory>

class MyObject
{
public:
    MyObject() : ptr(std::unique_ptr<int>(new int))
    {
    }

    MyObject(MyObject&& other) : ptr(std::move(other.ptr))
    {
    }

    MyObject& operator=(MyObject&& other)
    {
        ptr = std::move(other.ptr);
        return *this;
    }

private:
    std::unique_ptr<int> ptr;
};

int main(int argc, char* argv[])
{
    std::vector<MyObject> s;
    for (int i = 0; i < 5; ++i)
    {
        MyObject o;
        s.push_back(o);
    }

    return 0;
}

The push_back() function takes its argument by value. Therefore, an attempt is made to either copy-construct the argument of push_back() (if you are passing an lvalue), or to move-construct it (if you are passing an rvalue).

In this case, o is an lvalue - because named objects are lvalues - and rvalue references cannot bind to lvalues. Therefore, the compiler cannot invoke your move constructor.

In order to have your object moved , you have to write:

s.push_back(std::move(o));
//          ^^^^^^^^^

What surprises me in this case is that it seems VC11 generated a copy-constructor for MyObject implicitly without defining it as deleted (judging from the error you posted). This should not be the case, since your class declares a move constructor. Per Paragraph 12.8/7 of the C++11 Standard, in fact:

If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted ; otherwise, it is defined as defaulted (8.4)

I must conclude that while the error you are getting is correct - because you are not passing an rvalue to push_back() - VC11 is not fully compliant here.

MyObject o; defines o to be an object. Which means it's a l-value. Doing s.push_back(o); then invokes the l-value overload of push_back() (it has no other choice), which tries to create a copy.

Since your class is noncopyable, you have to move the object into the vector:

for (int i = 0; i < 5; ++i)
{
    MyObject o;
    s.push_back(std::move(o));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM