简体   繁体   中英

Deleting move constructor and constructing object from rvalue

I'm trying to understand Item 17 from "Effective Modern C++" about special member function generation so I was trying some examples and am trying to reason about some behavior. In the book it says:

..that when I refer to a move operation move-constructing or move-assigning a data member or base class, there is no guarantee that a move will actually take place. “Memberwise moves” are, in reality, more like memberwise move requests, because types that aren't move-enabled (ie, that offer no special support for move operations, eg, most C++98 legacy classes) will be “moved” via their copy operations. ... Furthermore, move operations won't be generated for any class that explicitly declares a copy operation.

The code below errors out if I explicitly delete the move constructor but if I don't the object "s1" gets copy constructed without any errors. Here's a wandbox link to the same code: wandbox link . I guess I'm not understanding the difference between deleting the move constructor and not defining one.

#include <iostream>

struct S
{
    S() = default;
    S(const S&) {
        std::cout << "Copying" << std::endl;
    }
   // S(S&&) = delete;
};

S return_lvalue() {
    S ret{};
    return ret;
}

int main() {
    std::cout << "Hello world" << std::endl;
    // Error here if I delete move constructor
    S s1 = return_lvalue();
}

I guess I'm not understanding the difference between deleting the move constructor and not defining one.

When you do

struct S
{
    S() = default;
    S(const S&) {
        std::cout << "Copying" << std::endl;
    }
};

The compiler will not generate a move constructor. If you try to move it, overload resolution will only find S(const S&) and you'll get a copy. With

struct S
{
    S() = default;
    S(const S&) {
        std::cout << "Copying" << std::endl;
    }
    S(S&&) = delete;
};

When you move an object of type S , overload resolution finds S(const S&) and S(S&&) but it picks S(S&&) since it is the better match. Then it sees that it is deleted and you get an error.

The thing you need to remember is deleted constructors don't remove them from the class. It declares them and makes them available for overload resolution and it is only after overload resolution happens, that it checks if it is deleted.

Deleting a special member function isn't the same as not declaring it. It's the same as declaring it then forcing a compilation error when you use it.

As such, there's not a huge amount of benefit in deleting a move ctor… unless you've also deleted the copy ctor for some reason, but then just don't declare a move ctor at all.

This doesn't have much to do with the quote, which is saying if you do declare a move ctor but don't do any "movey things" in it then ultimately nothing of value really got moved, which may be contrary to the expectations of your users.

I suggest you leave your move ctor undeclared. Again, this is not the same as it being deleted. And one will not be auto-generated because you have a copy ctor.

Find more technical information here:

Note that your program compiles in both cases in C++17 mode, because of elision.

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