3

Possible Duplicate:
Move constructor signature

struct X { X(X&); // (1) X(X&&); // (2) X(const X&); // (3) X(const X&&); // (4) }; 

Are there any situations where (4) will be picked in overload resolution?

2

2 Answers 2

9

Yes. One situation is when you have a function with a const return value:

const X f(); X x(f()); 
Sign up to request clarification or add additional context in comments.

8 Comments

Your answer is correct - but a const return value makes no sense right? Is there ever a use of making a return value const?
No, it doesn't make much sense. Some people got in the habit of doing it to avoid nonsensical things like f() = x in C++03, but nowadays that habit has to go away because it inhibits move semantics.
@R.MartinhoFernandes I believe there are cases where it does make sense. If you have time, please check my answer and the comments below it, and correct me if I am wrong. Thank you in a advance.
There are cases where it makes sense, when overloading + for instance, you don't want to allow x + y = z.
@K-ballo but you want to allow foo(x+y) to move, no?
|
2

Another situation is when you are applying std::move to a const object, as in the following example:

#include <iostream> using namespace std; struct X { X() { cout << "default" << endl; } X(X&) { cout << "non const copy" << endl; } // (1) X(X&&) { cout << "non const move" << endl; } // (2) X(const X&) { cout << "const copy" << endl; } // (3) X(const X&&){ cout << "const move" << endl; } // (4) }; void f(X const x) { } int main() { X const x; f(std::move(x)); return 0; } 

The case mentioned in a previous answer (X const f()) is probably less common, because the move constructor is most of the times elided by the compiler when doing RVO (this can be done even if the constructor has side-effects).

Moving a const object into another const object as a logical operation does make sense, and if you do not define a const move constructor then you won't be allowed to do that. Moving is just transferring ownership, so it seems to me that as a logical operation it should be supported.

Although it is true that from a high-level viewpoint you are not supposed to modify a const object and indeed moving an object requires modifying it (in most cases), there are well-known similar situations where it is OK to modify a const object "behind the scenes" for the purpose of realizing a higher-level conceptual operation. Think for instance of a const member function that needs to lock a non-const member mutex: you may declare the mutex variable as mutable to treat it as a non-const object.

So even in this case I see it as a legitimate operation in a const move constructor (as long as it is needed) to modify mutable members of the moved object (perhaps some boolean flag to be checked by the object's destructor). Please notice, that if you remove the const constructors (3) and (4) from the code above, then it won't compile. If you remove only (4), the copy constructor (3) will be chosen - meaning you won't be able to move the const object.

26 Comments

You seem to be implying that a const move contructor should const_cast away the constness and then do a normal move. I don't think this is const correct at all. A const move constructor should do exactly the same as the copy constructor. It makes no sense to move a const object.
Modifying an object whose original declaration is const is Undefined Behavior. So a safe const move constructor would not change the source in any circumstances. But then it's the same as the copy constructor, and there's no point in declaring the overload.
@aschepler Sorry, I don't think that is true. A const object may have mutable members that can be modified by a const move constructor. As I mentioned in a previous comment, the pattern is the same as when you want to lock a member mutex inside a const function.
Concrete example (sorry for bad formatting, don't know how to improve it): struct X { [...] X(const X&& other){ other.flag = false; cout << "const move" << endl; } mutable bool flag = true; }; This code is legal.
This is not completely correct: you may modify mutable values of a const objects. That makes it possible to move from a constant source. For instance I can imagine a vector class (not std::vector of course) whose const move constructor simply copies the data pointer and the size counter (and other stuff if necessary), and sets a mutable flag in the moved object to false, so that the destructor of that object won't try to deallocate the memory. No element-wise copy will occur, no double deallocation, so that's an effective, safe, and legal move of such a const vector. Am I wrong?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.