How to define a move constructor?
MSVC++ implemented move constructors before the final version of the standard was out. In the version of the standard MSVC++'s implementation was based on, the rules for generating a default move constructor were ridiculously more strict than they are in the final version of the standard. See here: Why is this code trying to call the copy constructor? (specifically this answer and the comments on it) for more info on that. This has not been and will not be fixed in Visual Studio 11,
for some unknown stupid reasonbecause they had other priorities.No, you need to call
std::move
on the members ofrcOther
, and you initialise members with the corresponding members from the dying object (you misnamedmiSize
):MyClass( MyClass&& rcOther )
: mpiSize( std::move(rcOther.mpiSize) )
, miSize2( std::move(rcOther.miSize2) )
{
rcOther.mpiSize = 0;
}It doesn't make a difference for built in types like
int
andint*
, but it definitely makes a difference for user-defined types.- The reason for this is that
std::move
just returns the argument casted into aT&&
, an rvalue-reference, so that the correct constructor (the move constructor,T(T&&)
) is called for each of the sub-objects. If you don't usestd::move
on the members of the dying object, they will be treated likeT&
, and the copy constructor of your subobjects (T(T&)
) will be called instead of the move constructor. That is very bad and thwarts almost the entire purpose of you having written a move constructor.
- The reason for this is that
3. You are doing some unnecessary things, like setting the integer to 0. You only need to set the pointer to 0 so that delete
ing it won't delete the resource of the new object you created.
Also, if this is not a didactic exercise, you may want to consider using std::unique_ptr
instead of managing the lifetime of your own object. This way you don't even have to write a destructor for your class. Note that if you do that, using std::move
to initialise the member from the dying member in the move constructor would be mandatory.
- Konrad Rudolph in his answer has caught the fact that your class manages a non-automatic resource but does not follow the Rule of
FiveThree, Four, or Five. See his answer for more details on this.
How to properly define a move constructor?
The proper generic way is to move-construct each member, but that's what the defauted version does anyway:
T(T && rhs)
: a(std::move(rhs.a))
, b(std::move(rhs.b))
{ }
As a rough rule, you should use the default definition if this is all you need, and you should write an explicit move constructor if you're doing something that explicitly implements move semantics, such as a unique-ownership resource manager:
URM(URM && rhs)
: resource(rhs.resource)
{
rhs.resource = nullptr;
}
The indicator for whether this is appropriate is probably whether your class has a user-defined destructor. In the example, the destructor would release the managed resource, and this must happen only once, so the moved-from object must be modified.
This is unrelated, but since you are mentioning the assignment operator, here's the popular swap-and-assign/swap idiom:
void swap(URM & rhs) noexcept // assume members are noexcept-swappable!
{
using std::swap;
swap(resource, rhs.resource);
// ...
}
URM & operator=(URM rhs) noexcept // pass by value
{
rhs.swap(*this);
return *this;
}
The beauty of this approach is that you only need one single version of the assignment operator that works for temporaries and non-temporaries alike, using move construction when appropriate, and as long as all your members are well-designed, you also only need one single swap
function. On top of that, if the swap function doesn't throw (which a well-designed class should allow), then your assignment operator doesn't throw, since all the possible exceptions would already occur at the call site.
Best C++ move constructor implementation practice
One way is to implement the default constructor, copy constructor and swap
function.
And then implement the move constructor, copy and move assignment operators using the first three.
E.g.:
struct X
{
X();
X(X const&);
void swap(X&) noexcept;
X(X&& b)
: X() // delegate to the default constructor
{
b.swap(*this);
}
// Note that this operator implements both copy and move assignments.
// It accepts its argument by value, which invokes the appropriate (copy or move) constructor.
X& operator=(X b) {
b.swap(*this);
return *this;
}
};
If you have been using this idiom in C++98, then once you add the move constructor you get the move assignment without writing a single line of code.
In some cases this idiom may be not the most efficient. Because the copy operator always first constructs a temporary and then swaps with it. By hand coding the assignment operators it may be possible to get better performance. When in doubt, check optimized assembly output and use a profiler.
Which copy/move constructor/operator to define for simple structures?
Q1: Can you rely on the compiler to auto-generate those?
Yes (in your example). See the C++11 Standard (clause 12), or the article Implicit Move Won’t Go! (nice diagram near the end). To sum up (and simplify), all the following special member functions will be auto-generated (implicitly declared and defined as defaulted):
- Destructor – because you didn't declare it.
- Copy Constructor – because you didn't declare it nor any of MC and MAO.
- Copy Assignment Operator – because you didn't declare it nor any of MC and MAO.
- Move Constructor – because you didn't declare it nor any of D, CC, CAO and MAO.
- Move Assignment Operator – because you didn't declare it nor any of D, CC, CAO and MC.
(I used ugly initials only to keep the list items one line each.) In addition to the "because"s above, for all but the Destructor there's the additional constraint that the generated defaults must make sense, i.e. all the data members must be copyable (for the CC and CAO) or movable (for the MC and MAO). (Actually the precise rules are a bit more complicated, but I don't want to rephrase the Standard here.)
Q2: Are the auto-generated functions correct?
Yes (in your example). All your data members (here plain int
s) have correct copy/move semantics (their copy/move constructors and assignment operators do the right thing, and those auto-generated for Rect
will call them).
Q3: Anyway, should you define them manually?
I see no advantage to it (in your example), and potential issues (as in your example, see comments).
Implicitly generate move constructor
This doesn't compile, because the implicitly declared copy constructor cannot copy p_. (12.8/7)
There is no need for a copy constructor. This does not compile because your compiler does not seem to generate a move constructor automatically, which it should.
There is no way around that other than implementing it yourself or updating the compiler.
Do move constructors and move assignment operators make sense in classes that don't hold pointers as member variables and don't manage resources?
An explicit move constructor or a move operator can be specified for any class, whether it has pointer members or not. It is true that move constructors and operators are usually used with classes that have pointers of some kind. But that's only because in these cases there's a way to effect a move that avoids the more expensive overhead of copying.
A default move operation for a trivial type is equivalent to a copy operation.
A default move operation for a class is equivalent to a move operation for each member of the class.
Therefore, in your example, the move operation is equivalent to a copy operation, and has no observable differences.
However it is certainly possible to need a move operator for a class that has no pointer members, for whatever reason. A classical example is std::thread
, which has a move constructor and an assignment operator but not the equivalent copy constructor and copy assignment operator. The contents of std::thread
are implementation defined, but typically consist of an opaque handle for an operating system-specific thread identifier. This handle cannot be "copied" in any meaningful way, but its ownership can be meaningful transferred to another std::thread
, and that's why you have move operators there.
Your own classes may have similar semantical requirements, too, even if they don't have any pointers of any kind.
How does the move constructor look like if I have a vector (or anything like it) member variable?
Whatever a std::vector
needs to do in order to move correctly, will be handled by its own move constructor.
So, assuming you want to move the member, just use that directly:
Shoplist(Shoplist&& other)
: slist(std::move(other.slist))
{}
and
Shoplist& Shoplist::operator=(Shoplist&& other)
{
slist = std::move(other.slist);
return *this;
}
In this case, you could as AndyG points out, just use = default
to have the compiler generate exactly the same move ctor and move assignment operator for you.
Note that explicitly destroying the original as you did is definitely absolutely wrong. The other
member will be destroyed again when other
goes out of scope.
Edit: I did say assuming you want to move the member, because in some cases you might not.
Generally you want to move data members like this if they're logically part of the class, and much cheaper to move than copy. While std::vector
is definitely cheaper to move than to copy, if it holds some transient cache or temporary value that isn't logically part of the object's identity or value, you might reasonably choose to discard it.
Understanding the reasoning between copy/move constructors and operators
Congratulations, you found a core issue of C++!
There are still a lot of discussions around the behavior you see with your example code.
There are suggestions like:
A&& helper_alt(A a) {
std::cout << ".." << std::endl;
return std::move(a);
}
This will do what you want, simply use the move assignment but emits a warning from g++ "warning: reference to local variable 'a' returned", even if the variable goes immediately out of scope.
Already other people found that problem and this is already made a c++ standard language core issue
Interestingly the issue was already found in 2010 but not solved until now...
To give you an answer to your question "In the last case, why is the move constructor being called and then the move assignment operator, instead of just the move assignment operator?" is, that also C++ committee does not have an answer until now. To be precise, there is a proposed solution and this one is accepted but until now not part of the language.
From: Comment Status
Amend paragraph 34 to explicitly exclude function parameters from copy elision. Amend paragraph 35 to include function parameters as eligible for move-construction.
Move Constructor & Move Assignment
Since this question was answered in the comments I thought I'd follow the advice from the meta: Question with no answers, but issue solved in the comments (or extended in chat) and write a short Community Wiki to close and answer the question.
I will also add useful additional info and tips from other users who joined the discussion in the comments.
Bo Presson answering and providing additional info on template placement:
The move assignment seems reasonable, except that putting templates in
a cpp file makes them usable in that cpp file only. See
Why can templates only be implemented in the header file?
Rakete1111 clarifying a misconception I had regarding move semantics:
std::move != move semantics. You have move semantics, where rvalues
can be moved (using the move constructor) instead of copied. std::move
is just a facility to enable move semantics (like using the move
constructor) for types that are not rvalues.
kim366 bringing up return optimization question with Jive Dadson and I answering:
... Also, is there really no return-value-optimization, if you don't
have overloaded move ctors/assignments ? -kim366It seems so, in the example (see function below) he says that
z = x +
will copy the return result twice "If a Vector is large, say,
y + z
10,000 doubles, that could be embarrassing." But "Given that
definition, the compiler will choose the move constructor to implement
the transfer of the return value..." He invented c++ so ill just take
his word for it :).Vector operator+(const Vector& a, const Vector&
- hammeramr
b) { if (a.size()!=b.size()) throw Vector_size_mismatch{}; Vector
res(a.size()); for (int i=0; i!=a.size(); ++i) res[i]=a[i]+b[i];
return res; }(Example was from the book: "The C++ programing language 4th edition"
by Bjarne Stroustrup)See also
What is the copy-and-swap idiom?
-Jive Dadson
Hope people find this useful and thanks for those who participated.
What does explicitly-defaulted move constructor do?
What does explicitly-defaulted move constructor do?
It will initialize its member variables with the corresponding member variables in the moved from object after turning them into xvalues. This is the same as if you use std::move
in your own implementation:
Base(Base&& other) noexcept : id(std::move(other.id)) {}
Note though that for fundamental types, like int
, this is no different from copying the value. If you want the moved from object to have its id
set to 0
, use std::exchange
:
Base(Base&& other) noexcept : id(std::exchange(other.id, 0)) {}
Base& operator=(Base&& other) noexcept {
if(this != &other) id = std::exchange(other.id, 0);
return *this;
}
With that, the Foo
move constructor and move assignment operator can be default
ed and "do the right thing".
Suggestion: I think Base
would benefit from a constructor that can initialize id
directly. Example:
Base(int Id) : id(Id) {
std::cout << "Base() called " << id << std::endl;
}
Base() : Base(0) {} // delegate to the above
Now, the Foo
constructor taking an id as an argument could look like this:
Foo(int Id) : Base(Id) {
std::cout << "Foo() called " << id << std::endl;
}
Related Topics
Clean Up Your #Include Statements
Getprocaddress Function in C++
Why Function Objects Should Be Pass-By-Value
Std Linker Error with Apple Llvm 4.1
Multiplying a String by an Int in C++
Convert a Number to a String Literal with Constexpr
How to Pre-Allocate Space for a File in C/C++ on Windows
Programmatically Selecting File in Explorer
How to Create a Type List (For Variadic Templates) That Contains N-Times the Same Type
Differencebetween Static_Cast and Implicit_Cast
How to Vectorize My Loop with G++
Making Qlabel Behave Like a Hyperlink