简体   繁体   中英

Why does the C++11 move constructor copy by value?

I thought the following example would have the same result as std::map::emplace() , but it didn't:

struct MoveTest {
    MoveTest():
        var(true)
    {
        cout << "ctor: " << static_cast<void*>(&var) << endl;
    }

    MoveTest(MoveTest&& mt):
        var(std::move(mt.var))
    {
        cout << "move ctor: " << static_cast<void*>(&var) << endl;
    }

    bool var;
};


int main() {
    map<int, MoveTest> mtest;
    mtest.insert(make_pair(1, MoveTest()));
    return 0;
}

output:

ctor: 0x7fff12e4e19b
move ctor: 0x7fff12e4e194
move ctor: 0x25f3034

The MoveTest::var has a different address in each move. It doesn't look like a " move ". What's wrong in my code or understanding?

For MoveTest as you've defined it, there's not really much a move ctor (or assignment operator) can optimize (or do in general--all it can really do is copy from source go destination).

Moving is mostly a big win when an object contains a pointer to a bunch of external memory that's (for example) allocated on the heap. In this case, the move ctor/assignment operator can basically do a shallow copy (ie, just grab the pointer from the moved-from object) instead of a deep copy (copying all the data referred to by that pointer).

For example:

class move_tst {
    int *buffer;
    static const int size = 1024 * 1024;
public:
    move_tst() {        
        buffer = new int[size];
        std::iota(buffer, buffer + size, 0);
    }

    move_tst(move_tst const &other) {
        buffer = new int[size];
        std::copy_n(other.buffer, size, buffer);
    }

#ifdef MOVE
    move_tst(move_tst &&other) {
        buffer = other.buffer;
        other.buffer = nullptr;
    }
#endif

    ~move_tst() { 
        delete [] buffer;
    }
};

Caveat: I've used a raw invocation of new and a raw pointer here so nothing else gets involved and we wouldn't (for example) get move semantics courtesy of a smart pointer. For normal code under normal circumstances, you should not be using either (raw pointer or raw invocation of new , I mean).

Edit: as far as what std::move does, it doesn't actually do a move itself--it just signals that you no longer care about a value, so it's eligible to be the source of a move, even if that destroys its value.

For example, with the class above, you could do a test like:

for (int i = 0; i < 1000; i++) {
    move_tst src;
    move_tst dst =src;
}       

...and compare it to:

for (int i = 0; i < 1000; i++) {
    move_tst src;
    move_tst dst = std::move(src);
}       

Without the std::move , dst will be created as a copy of src , but with the std::move , it'll be able to do a move from src to dst .

Your log simply tells that you create a temporary MoveTest instance with the default constructor

MoveTest()

which is then copied elsewhere, inside a pair object

make_pair(1, MoveTest())

and copied again somewhere inside the map object

mtest.insert(make_pair(1, MoveTest()));

There are three constructor invocations for three distinct MoveTest instances, each with its own address. As already well explained, whether the constructors are move constructors or old style copy constructors doesn't matter.

Did you expect some of the copies to be optimized away? With nontrivial code in the constructors it becomes unlikely; in particular, reading &var is a good reason to avoid shortcuts.

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