Reputation: 855
I would like to fully understand move semantics in C++11. So I have written a couple of classes to see when different constructors are called:
#include <iostream>
using namespace std;
class A {
public:
A() : a1_(0) {std::cout << "Calling constructor" << std::endl;}
A(A&& other) {
std::cout << "Calling move constructor" << std::endl;
a1_ = other.a1_;
other.a1_ = 0;
}
// Move assignment operator.
A& operator=(A&& other) {
std::cout << "Calling move operator" << std::endl;
if (this != &other) {
a1_ = other.a1_;
other.a1_ = 0;
}
return *this;
}
// Copy constructor.
A(const A& other) {
std::cout << "Calling copy constructor" << std::endl;
a1_ = other.a1_;
}
// Copy assignment operator.
A& operator=(const A& other) {
std::cout << "Calling copy assignment operator" << std::endl;
if (this != &other) {
a1_ = other.a1_;
}
return *this;
}
private:
int a1_;
};
class B {
A oA_;
public:
B() {}
void setoA(A a) {oA_ = a;}
A getoA() {return oA_;}
};
A createA() {
A a1;
return a1;
}
B createB() {
B tmpB;
A tmpA;
tmpB.setoA(tmpA);
return tmpB;
}
int main() {
B b;
A a;
b.setoA(a);
std::cout << "**************************" << std::endl;
b.setoA(createA());
std::cout << "**************************" << std::endl;
b.setoA(std::move(createA()));
std::cout << "**************************" << std::endl;
B b2;
b2.setoA(b.getoA());
std::cout << "**************************" << std::endl;
createB();
return 0;
}
When I check the output of this code:
Calling constructor Calling constructor Calling copy constructor Calling copy assignment operator ++++++++++++++++++++++++++++++++++ Calling constructor Calling copy assignment operator ++++++++++++++++++++++++++++++++++ Calling constructor Calling move constructor Calling copy assignment operator ++++++++++++++++++++++++++++++++++ Calling constructor Calling copy constructor Calling copy assignment operator ++++++++++++++++++++++++++++++++++ Calling constructor Calling constructor Calling copy constructor Calling copy assignment operator
I have some doubts here:
I thought that if you pass r-value
, move constructor will be called, is that right? Isn't this b.setoA(createA());
an r-value
?
How can I make move constructor/operator being called?
Upvotes: 0
Views: 319
Reputation: 119184
Some copies and moves may be optionally elided by the compiler. To prevent this, use -fno-elide-constructors
in GCC and Clang. In addition, some move elisions became mandatory in C++17, so to force the compiler to use C++11 move semantics, use -std=c++11
as well.
Upvotes: 0
Reputation: 25526
First of all in first section, why is constructor being called twice?
Because you construct both an B
and a A
with the former having its own instance of A
, which the first (the unexpected) constructor call comes from.
I thought that if you pass r-value move constructor will be called, is that right? Isn't this
b.setoA(createA());
an r-value?
The constructor is called from within createA
(and yes, return value is an r-value), however, copy elision occurs and the object is directly instantiated in the parameter variable of setoA
.
Within setoA
, however, the copy assignment is selected, as now a
is an l-value. If you want to move, you need:
void setoA(A a) { oA_ = std::move(a); }
Upvotes: 6