C++11 std::move – Why Use std::move on a const Object?

c++c++11

In C++11, we can write this code:

struct Cat {
   Cat(){}
};

const Cat cat;
std::move(cat); //this is valid in C++11

when I call std::move, it means I want to move the object, i.e. I will change the object. To move a const object is unreasonable, so why does std::move not restrict this behaviour? It will be a trap in the future, right?

Here trap means as Brandon mentioned in the comment:

" I think he means it "traps" him sneaky sneaky because if he doesn't
realize, he ends up with a copy which is not what he intended."

In the book 'Effective Modern C++' by Scott Meyers, he gives an example:

class Annotation {
public:
    explicit Annotation(const std::string text)
     : value(std::move(text)) //here we want to call string(string&&),
                              //but because text is const, 
                              //the return type of std::move(text) is const std::string&&
                              //so we actually called string(const string&)
                              //it is a bug which is very hard to find out
private:
    std::string value;
};

If std::move was forbidden from operating on a const object, we could easily find out the bug, right?

Best Answer

There's a trick here you're overlooking, namely that std::move(cat) doesn't actually move anything. It merely tells the compiler to try to move. However, since your class has no constructor that accepts a const CAT&&, it will instead use the implicit const CAT& copy constructor, and safely copy. No danger, no trap. If the copy constructor is disabled for any reason, you'll get a compiler error.

struct CAT
{
   CAT(){}
   CAT(const CAT&) {std::cout << "COPY";}
   CAT(CAT&&) {std::cout << "MOVE";}
};

int main() {
    const CAT cat;
    CAT cat2 = std::move(cat);
}

prints COPY, not MOVE.

http://coliru.stacked-crooked.com/a/0dff72133dbf9d1f

Note that the bug in the code you mention is a performance issue, not a stability issue, so such a bug won't cause a crash, ever. It will just use a slower copy. Additionally, such a bug also occurs for non-const objects that don't have move constructors, so merely adding a const overload won't catch all of them. We could check for the ability to move construct or move assign from the parameter type, but that would interfere with generic template code that is supposed to fall back on the copy constructor. And heck, maybe someone wants to be able to construct from const CAT&&, who am I to say he can't?

Related Question