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?
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); }
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.
Related
I have the simple program below:
#include <iostream>
class Counter
{
private:
size_t m_count;
public:
Counter() :
m_count(0)
{
std::cout << "defctor" << '\n';
}
Counter(size_t count) :
m_count(count)
{
std::cout << "ctor" << '\n';
}
~Counter() {
std::cout << "dtor" << '\n';
}
Counter(const Counter& rhs) :
m_count{0}
{
Counter temp{rhs.m_count};
std::cout << "cctor" << '\n';
std::swap(*this, temp);
}
Counter& operator=(const Counter& rhs)
{
if (this == &rhs) {
return *this;
}
Counter temp{rhs.m_count};
std::swap(*this, temp);
std::cout << "caop" << '\n';
return *this;
}
constexpr int getCount() const noexcept {
return this-> m_count;
}
};
int main() {
Counter x{1};
Counter y;
Counter z{x}; // this fails
}
I've tried to construct a simple rule of three class. I get UB on this line Counter z{x}; which should be calling the copy constructor. Output:
ctor
defctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
Then it repeats ctor\ncctor ...
It's been a while since I worked with C++. I just cannot find the error!
std::swap only uses the move assignment operator if you have defined one (the compiler will not define one once you add almost any other special member function). You did not, so it falls back to copy assignment.
But your copy assignment operator is defined in terms of std::swap. That's of course an endless recursion: To swap you need to assign, and to assign you need to swap. Eventually you get a stack overflow.
You could just initialize m_count directly in the copy constructor (and modify it directly in the copy assignment operator). It looks like you are doing half a copy-and-swap idiom, but I'm not sure what you are really going for here.
And yes, in modern C++ you should also implement the move constructor where appropriate. If done correctly, this would fix the std::swap recursion. I suggest you look into some examples of how to implement these special member functions correctly.
In my opinion you should change your copy constructor like this:
Counter(const Counter& rhs)
{
m_count=rhs.m_count;
}
It will be enough. The result is:
ctor
defctor
dtor
dtor
dtor
Press any key to continue ...
This is my code:
#include <iostream>
using namespace std;
class A
{
int i;
public:
A(int v) : i(v) { }
A(const A& r) : i(r.i) {
cout << "Copy constructor" << endl;
}
A operator=(const A& r) {
cout << "Assignment function" << endl;
return r;
}
void show() {
cout << i << endl;
}
};
int main()
{
A a(1);
A b(2);
a = b;
a.show();
return 0;
}
Value of b is 2 and value of a is 1. In 'main', b is copied into a and this the output I get:
Assignment function
Copy constructor
This is understandable, but the output for a.show() comes out be 1. I can't understand this. How? Because b is copied into a using the copy constructor so shouldn't a.i have the value of b.i?
b is copied into a using the assignment operator, not the copy constructor. And your assignment operator doesn't assign i, so a.i keeps its original value.
The copy constructor you are seeing is for the return value of operator=. It is more customary to return the left-hand-side by reference, not the right-hand-side by value:
A& operator=(const A& r) {
cout << "Assignment function" << endl;
i = r.i;
return *this;
}
When you define the assignment operator you have to do all of the work to copy the data.
A operator=(const A& r) {
cout << "Assignment function" << endl;
i = r.i;
return r;
}
so a = b is calling your assignment operator (obviously, hence your output). But right now it isn't copying anything.
The assignment operator doesn't do anything to the "assigned to" object. The copy constructor call you see reported is from the creation of the assignment return value.
Your copy assignment should probably look loke this:
A& A::operator= (A const& other) {
// output
this->i = other.i;
return *this;
}
I have extended std::string to fulfil my needs of having to write custom function build into string class called CustomString
I have defined constructors:
class CustomString : public std::string {
public:
explicit CustomString(void);
explicit CustomString(const std::string& str);
explicit CustomString(const CustomString& customString);
//assignment operator
CustomString& operator=(const CustomString& customString);
... };
In the third constructor (copy constructor) and assignment operator, whose definition is:
CustomString::CustomString(const CustomString& customString):
std::string(static_cast<std::string>(customString))
{}
CustomString& CustomString::operator=(const CustomString& customString){
this->assign(static_cast<std::string>(customString));
return *this;
}
First since this is an "explicit"; meaning an explicit cast is needed to assign to another CustomString object; it's complaining about the assignment.
CustomString s = CustomString("test");
I am not sure where exactly is casting needed explicitly.
The code works alright if copy constructor is not explicit but I would like to know and implement explicit definition instead of "guessing proper cast".
The explicit copy constructor means that the copy constructor will not be called implicitly, which is what happens in the expression:
CustomString s = CustomString("test");
This expression literally means: create a temporary CustomString using the constructor that takes a const char*. Implicitly call the copy constructor of CustomString to copy from that temporary into s.
Now, if the code was correct (i.e. if the copy constructor was not explicit), the compiler would avoid the creation of the temporary and elide the copy by constructing s directly with the string literal. But the compiler must still check that the construction can be done and fails there.
You can call the copy constructor explicitly:
CustomString s( CustomString("test") );
But I would recommend that you avoid the temporary altogether and just create s with the const char*:
CustomString s( "test" );
Which is what the compiler would do anyway...
Deriving from std::string is not safe, as std::string has no virtual destructor. As to your question - your copy constructors should not be explicit, to allow for such usage as:
CustomString s = "test";
Also I have no idea why you would want to declare a copy-constructor as explicit, as it is not needed.
An explicit copy-constructor will work only if you declare your CustomString object as:
CustomString s(CustomString("test"));
It seems current gcc won't invoke the copy constructor tested in gcc 8.2(MinGW.org GCC-8.2.0-5),and will utilize to call constructor directly For X1
#include <iostream>
using std::cout;
using std::endl;
class X1 {
public:
X1() {
cout<< "invoke constructor" << endl;
};
explicit X1(const X1 &obj) {
cout << "invoke copy constructor" << endl;
};
X1(X1&& obj) {
cout << "invoke move constructor" << endl;
};
X1& operator=(const X1 &obj) {
cout << "invoke value assign constructor " << endl;
if(this == &obj) {
return *this;
}
return *this;
};
X1& operator=(const X1 && obj) {
cout << "invoke move value assign constructor " << endl;
if(this == &obj) {
return *this;
}
return *this;
};
~X1() {
cout << "invoke deconstruct" << endl;
};
};
int main() {
// X1 l1(1);
// X1 l3(2);
X1 l4 = X1();
return 0;
}
#include <iostream>
class C {
public:
~C() { std::cout << this << " destructor\n"; }
C() { std::cout << this << " constructor\n"; }
C(C&& rhs) {
std::cout << &rhs << " rhs\n";
std::cout << this << " move constructor\n";
}
C& operator=(C&& rhs) {
std::cout << &rhs << " rhs\n";
std::cout << this << " move assignment\n";
return *this;
}
};
C make_one() {
C tmp;
return tmp;
}
int main() {
std::cout << "move constructor:\n";
C c1(make_one());
std::cout << &c1 << " &c1\n\n";
std::cout << "move assignment:\n";
C c2;
c2 = make_one();
...
}
Output:
move constructor:
000000000021F9B4 constructor // tmp constructed in make_one()
000000000021F9B4 rhs // return triggers tmp being passed to ...
000000000021FA04 move constructor // ... c1's move constructor (see below)
000000000021F9B4 destructor // tmp destructs on going out of scope
000000000021FA04 &c1 // (confirmed c1's address)
move assignment:
000000000021FA24 constructor // c2 constructed
000000000021F9B4 constructor // tmp constructed in make_one() again
000000000021F9B4 rhs // tmp passed to ...
000000000021FA34 move constructor // ... a new object's move constructor
000000000021F9B4 destructor // tmp destructs on going out of scope
000000000021FA34 rhs // new object passed to ...
000000000021FA24 move assignment // .. c2's move assignment operator
000000000021FA34 destructor // new object destructs
...
The move assignment appears to trigger the move constructor first and create an extra object. Is this normal? I would have expected (by analogy with copy assignment) for tmp to be passed straight to c2's move assignment.
[Visual Studio Express 2013]
The "extra object" is called the return value. When returning a value from a function; this value is copy/move constructed from the value you supplied to the return statement.
Often this undergoes copy elision which may explain why you didn't recognize it. When copy elision happens, the line C tmp; will actually construct tmp directly into the return value. Copy elision can also occur in some other situations; for the full text see C++11 [class.copy]#31.
Presumably you either manually disabled copy elision here, or the compiler decided it was a good idea not to perform copy elision. Update: Your compiler does this particular copy-elision only on Release builds - thanks Praetorian
I finished off my example and leave it here in case it helps anyone else.
#include <iostream>
class C {
public:
~C() { std::cout << this << " destructor\n"; }
C() { std::cout << this << " constructor\n"; }
C(const C& rhs) {
std::cout << &rhs << " rhs\n";
std::cout << this << " copy constructor\n";
}
C& operator=(const C& rhs) {
std::cout << &rhs << " rhs\n";
std::cout << this << " copy assignment\n";
return *this;
}
C(C&& rhs) {
std::cout << &rhs << " rhs\n";
std::cout << this << " move constructor\n";
}
C& operator=(C&& rhs) {
std::cout << &rhs << " rhs\n";
std::cout << this << " move assignment\n";
return *this;
}
};
C make_one() {
C tmp;
return tmp;
}
int main() {
std::cout << "c1's constructor:\n";
C c1;
std::cout << "\nc1 passed to c2's copy constructor:\n";
C c2(c1);
std::cout << &c2 << " &c2\n\n";
std::cout << "c3 and c4's constructors:\n";
C c3, c4;
std::cout << "c3 passed to c4's copy assignment:\n";
c4 = c3;
std::cout << "\ntmp constructed, passed to c5's move constructor then destructed\n";
C c5(make_one());
std::cout << &c5 << " &c5\n\n";
std::cout << "c6's constructor:\n";
C c6;
std::cout << "tmp constructed, passed to return value's move constructor then destructed\n"
"return value passed to c6's move assignment then destructed:\n";
c6 = make_one();
return 0;
}
Debug Output:
c1's constructor:
000000000013F9B4 constructor
c1 passed to c2's copy constructor:
000000000013F9B4 rhs
000000000013F9D4 constructor
000000000013F9D4 &c2
c3 and c4's constructors:
000000000013F9F4 constructor
000000000013FA14 constructor
c3 passed to c4's copy assignment:
000000000013F9F4 rhs
000000000013FA14 copy assignment
tmp constructed, passed to c5's move constructor then destructed
000000000013F964 constructor
000000000013F964 rhs
000000000013FA34 move constructor
000000000013F964 destructor
000000000013FA34 &c5
c6's constructor:
000000000013FA54 constructor
tmp constructed, passed to return value's move constructor then destructed
return value passed to c6's move assignment then destructed:
000000000013F964 constructor
000000000013F964 rhs
000000000013FA64 move constructor
000000000013F964 destructor
000000000013FA64 rhs
000000000013FA54 move assignment
000000000013FA64 destructor
...
Release Output (showing elision of move constructor):
c1's constructor:
00000000001BFC41 constructor
c1 passed to c2's copy constructor:
00000000001BFC41 rhs
00000000001BFC40 constructor
00000000001BFC40 &c2
c3 and c4's constructors:
00000000001BFC78 constructor
00000000001BFC70 constructor
c3 passed to c4's copy assignment:
00000000001BFC78 rhs
00000000001BFC70 copy assignment
tmp constructed, passed to c5's move constructor then destructed
00000000001BFC68 constructor
00000000001BFC68 &c5
c6's constructor:
00000000001BFC60 constructor
tmp constructed, passed to return value's move constructor then destructed
return value passed to c6's move assignment then destructed:
00000000001BFC42 constructor
00000000001BFC42 rhs
00000000001BFC60 move assignment
00000000001BFC42 destructor
....
I have a user-defined class (tree structure) with implemented move semantics, and a swap function. I would like to implement a move function the proper way, working as standard std::move implementation.
In the tree node class, each child node has a parent pointer, pointing to the parent node. This means that for move operations, all children have to reparented (and there may be many children)
This means that use of swap for moving is not optimal, as children of both lists have to be reparented after being swapped. So I would like to implement a move function which clears the moved-from tree.
The declaration of std::move implementations is somewhat complicated, they use a std::remove_reference<T>::type&& return type. Do I need this?
You don't need to write a specialisation of std::move.
If you write a correct move constructor and move assignment operator, std::move will work on your class.
example:
#include <iostream>
#include <cstring>
using namespace std;
struct Thing {
Thing()
: _data(new int[100])
{
cout << "default construct\n";
}
// Copy operator
Thing(const Thing& other)
: _data(new int[100])
{
cout << "copy constructor\n";
memcpy(_data, other._data, sizeof(int) * 100);
}
// Move constructor
Thing(Thing&& other) noexcept
: _data(other._data)
{
cout << "move constructor\n";
other._data = nullptr;
}
// assignment operator
Thing& operator=(const Thing& rhs) {
cout << "copy operator\n";
if (&rhs != this) {
Thing tmp(rhs);
std::swap(*this, tmp);
}
return *this;
}
// move assignment operator
Thing& operator=(Thing&& rhs) noexcept {
cout << "move operator\n";
std::swap(_data, rhs._data);
return *this;
}
// destructor necessary since we are working in dangerous new/delete territory
~Thing() noexcept {
cout << "destructor " << (_data ? "object has data" : "object is empty") << "\n";
delete[] _data;
}
private:
int* _data;
};
int main()
{
cout << "constructing a\n";
Thing a;
cout << "constructing b with copy of a\n";
Thing b(a);
cout << "moving a to newly constructed c\n";
Thing c(std::move(a));
cout << "moving c back to a\n";
a = std::move(c);
cout << "create a new d\n";
Thing d;
cout << "replace d with a copy of a\n";
d = a;
return 0;
}
Program's output:
constructing a
default construct
constructing b with copy of a
copy constructor
moving a to newly constructed c
move constructor
moving c back to a
move operator
create a new d
default construct
replace d with a copy of a
copy operator
copy constructor
move constructor
move operator
move operator
destructor object is empty
destructor object has data
destructor object has data
destructor object is empty
destructor object has data
destructor object has data
To write move semantics is to write move constructor/move assignment, but not to implement move function. As result, you maybe move the root of the another tree in move ctor/assignment.
class binary_tree_node {};
class binary_tree
{
public:
binary_tree() : root(nullptr) {}
binary_tree(binary_tree &&rhs)
: root(rhs.root)
{
rhs.root = nullptr;
}
binary_tree& operator=(binary_tree rhs)
{
swap(rhs);
return *this;
}
void swap(binary_tree &rhs)
{
std::swap(root, rhs.root);
}
private:
binary_tree_node *root;
};
int main()
{
binary_tree tree1;
binary_tree tree2 = std::move(tree1);
return 0;
}