C++ move assignment triggers move constructor first - c++

#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
....

Related

Will asigning a variable to another variable result in it using a copy constructor or being a reference?

I am a little confused with the copy constructor and references in c++.
Kat kat("hello kitty");
Kat newKat = kat;
Will Kat use its copy constructor or will newKat just become a reference to Kat.
Edit: sorry for the confusion, I mean for custom types.
Kat kat("hello kitty");
Kat newKat = kat;
Will use the copy constructor
Kat kat("hello kitty");
Kat& newKat = kat;
Will create a reference
Since when creating the Kat object you don't use the new keyword and your variable is not a reference type (See more) (or pointer) the object is being created on the Stack. Assigning objects from the stack will result in them getting copied, so it will use the copy constructor.
I think this can be a better answer to your question since you need to understand all of the special member functions and not just the copy constructor.
Now take a look at this:
#include <iostream>
#include <utility>
#include <string>
class Kat
{
public:
Kat( )
:msg( "Default Msg" )
{
std::clog << "default ctor" << '\n';
}
Kat( const std::string& message )
: msg( message )
{
std::clog << "parameterized ctor" << '\n';
}
Kat( const Kat& rhs ) // copy ctor
: msg( rhs.msg )
{
std::clog << "copy ctor" << '\n';
}
Kat& operator=( const Kat& rhs ) // copy assignment operator
{
if ( this != &rhs )
{
msg = rhs.msg;
}
std::clog << "copy assignment operator" << '\n';
return *this;
}
Kat( Kat&& rhs ) noexcept // move ctor
: msg( rhs.msg )
{
rhs.msg = "";
std::clog << "move ctor" << '\n';
}
Kat& operator=( Kat&& rhs ) noexcept // move assignment operator
{
if ( this != &rhs )
{
msg = rhs.msg;
rhs.msg = "";
}
std::clog << "move assignment operator" << '\n';
return *this;
}
private:
std::string msg;
};
int main( )
{
Kat kat1; // default ctor
Kat kat2( "hello kitty" ); // parameterized ctor
Kat kat3 = kat2; // copy ctor
Kat kat4( kat2 ); // also copy ctor
kat1 = kat2; // copy assignment operator
Kat kat5 = std::move( kat2 ); // move ctor
Kat kat6( std::move( kat3 ) ); // also move ctor
kat6 = std::move( kat1 ); // move assignment operator
return 0;
}
And the output:
default ctor
parameterized ctor
copy ctor
copy ctor
copy assignment operator
move ctor
move ctor
move assignment operator

Move semantics in C++11

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.

Why is copy constructor called here?

I have the following code:
template<class T = char>
class String
{
public:
// Default constructor
String()
: buffer(nullptr),
len(0)
{
cout << "Default constructor" << endl;
}
// Constructor
String(const char* s)
{
cout << "Constructor (const char*)" << endl;
//...
}
// Virtual destructor.
virtual ~String()
{
cout << "Destructor" << endl;
len = 0;
delete[] buffer;
}
// Copy constructor
String(const String& s)
{
cout << "Copy constructor" << endl;
buffer = new T[s.len];
std::copy(s.buffer, s.buffer + s.len, buffer);
len = s.len;
}
// Copy assignment operator (uses copy and swap idiom)
String& operator=(String s)
{
cout << "Copy assignment operator (copy and swap idiom)" << endl;
std::swap(buffer, s.buffer);
return *this;
}
// Move constructor
String(String&& s)
{
cout << "Move constructor" << endl;
}
// compound assignment (does not need to be a member,
// but often is, to modify the private members)
String& operator+=(const String& rhs)
{
cout << "operator+=" << endl;
//...
return *this; // return the result by reference
}
// friends defined inside class body are inline and are hidden from non-ADL lookup
// passing lhs by value helps optimize chained a + b + c
// otherwise, both parameters may be const references
friend String operator+(String lhs, const String& rhs)
{
cout << "operator+" << endl;
lhs += rhs; // reuse compound assignment
return lhs; // return the result by value (uses move constructor)
}
private:
T* buffer;
size_t len;
};
int main()
{
String<> s("Hello ");
String<> s2("World");
// call copy constructor first?
String<> s3 = s + s2;
return 0;
}
And the output is:
Constructor (const char*)
Constructor (const char*)
Copy constructor
operator+
operator+=
Move constructor
Destructor
My question is why is the copy constructor called immediately in:
String<> s3 = s + s2;
A value copy of s is taken by friend String operator+(String lhs, const String& rhs), essentially because s is not an anonymous temporary and is therefore not an appropriate candidate for move construction. Taking that value copy requires the copy constructor.
To my understanding, the copy constructor is called to receive the return value of the + operator.

Understanding C++ copy constructor behavior

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;
}

How to implement a c++11 move function for a user-defined class?

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;
}