The move assignment takes an object about to die and copies its values into an existing object. The object on the lhs isn't created and is simply overwritten. The object on the rhs is left in a valid but otherwise indeterminate state, just as a move constructor would leave its source.
The operator that does this transfer is written operator= and takes an rvalue reference argument. The following program demonstrates a move-assignment operator that moves two integers, then zeroes the source.
#include <iostream>
class Point
{
private:
int x{};
int y{};
public:
Point(int xVal, int yVal)
: x{ xVal }
, y{ yVal }
{
}
// Bind an rvalue reference to a temporary or movable object
Point& operator=(Point&& other)
{
x = other.x;
y = other.y;
other.x = 0;
other.y = 0;
std::cout << "move assignment\n";
return *this;
}
void print() const
{
std::cout << x << ',' << y << '\n';
}
};
int main()
{
Point a{ 3, 5 };
Point b{ 9, 9 };
b = static_cast<Point&&>(a);
a.print();
b.print();
return 0;
}
Two objects are created with the default constructor. The statement b = static_cast<Point&&>(a) will choose the move-assignment operator due to the right-hand side being an rvalue reference. Within this operator, the members of b are overwritten with the members of a, then the members of a are zeroed.
The message move assignment appears, before the two print calls where 0,0 is printed for a and 3,5 for b.
Object b was already there; it is just updated.
The same operator is chosen by default when the rhs is a temporary. A function returning a Point by value creates such a temporary.
#include <iostream>
class Point
{
private:
int x{};
int y{};
public:
Point(int xVal, int yVal)
: x{ xVal }
, y{ yVal }
{
}
// Define how this operator works for the class
Point& operator=(Point&& other)
{
x = other.x;
y = other.y;
other.x = 0;
other.y = 0;
std::cout << "move assignment\n";
return *this;
}
void print() const
{
std::cout << x << ',' << y << '\n';
}
};
Point makePoint()
{
return Point{ 7, 11 };
}
int main()
{
Point p{ 0, 0 };
p = makePoint();
p.print();
return 0;
}
The call makePoint() creates a temporary Point. That temporary is an rvalue, so the assignment p = makePoint() uses the move-assignment operator. The values from the temporary get moved into the already-existent object p, the temporary gets zeroed, and the message "move assignment" is printed. The final print shows 7,11.
Returning a reference to the left-hand object enables move assignment to be chained just like copy assignment. The expression a = b = std::move(c) first moves from c into b and then moves from b into a. Each move leaves its source in a valid empty like state.
An assignment operator with move semantics must also correctly handle the case where an object is assigned to itself via an rvalue reference. Though uncommon, a straightforward identity test can preserve the object:
#include <iostream>
class Point
{
private:
int x{};
int y{};
public:
Point(int xVal, int yVal)
: x{ xVal }
, y{ yVal }
{
}
Point& operator=(Point&& other)
{
if (this == &other)
{
return *this;
}
x = other.x;
y = other.y;
other.x = 0;
other.y = 0;
std::cout << "move assignment\n";
return *this;
}
void print() const
{
std::cout << x << ',' << y << '\n';
}
};
int main()
{
Point a{ 4, 8 };
// Perform this conversion explicitly
a = static_cast<Point&&>(a);
a.print();
return 0;
}
If the right-hand side refers to the same object as the left, the operator returns immediately. None of the members are changed, and nothing is printed. So, the object is still 4,8.
Move assignment in this way therefore varies from move construction in one important respect: the target object already exists, and thus may already own values. The operator first replaces these values with the values extracted from the source (the rvalue), then resets the source into a safe empty like state. For a class of mere integers, this transfer looks like a sequence of assignments followed by a reset of the source.
Exercise: Move Assignment OperatorsStarter code, hints and solutionOpenClose
Practice: Move Assignment Operators
Work through the mission, reveal hints only when needed, and compare your result with the model solution.
Your challenge
Construct c with (1,1), then move-assign b into it. Print c and b and distinguish ordinary construction from move assignment.
Mission checklist
- Run the starter as a baseline, then predict the change requested in Your challenge.
- Make that change in your editor; use the lesson-specific hints if you need help.
- Compile and run your answer, then compare with the worked solution and explain the result.
Starter program
This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.
#include <iostream>
class Point
{
private:
int x{};
int y{};
public:
Point(int xVal, int yVal)
: x{ xVal }
, y{ yVal }
{
}
// Bind an rvalue reference to a temporary or movable object
Point& operator=(Point&& other)
{
x = other.x;
y = other.y;
other.x = 0;
other.y = 0;
std::cout << "move assignment\n";
return *this;
}
void print() const
{
std::cout << x << ',' << y << '\n';
}
};
int main()
{
Point a{ 3, 5 };
Point b{ 9, 9 };
b = static_cast<Point&&>(a);
a.print();
b.print();
return 0;
}Hints
Both Points already exist before b = static_cast<Point&&>(a). Follow the move assignment body: b receives a's coordinates and then a is cleared.
The returned *this refers to b, the assignment target. Keep source and target distinct in this exercise; this minimal implementation has no self-move guard.
Worked solution
The solution adds another move assignment into an already-constructed c. c receives b's 3,5 and this class clears b to 0,0. Constructing a, b, and c from integers is ordinary construction, not copying or moving.
#include <utility>
#include <iostream>
class Point
{
private:
int x{};
int y{};
public:
Point(int xVal, int yVal)
: x{ xVal }
, y{ yVal }
{
}
// Bind an rvalue reference to a temporary or movable object
Point& operator=(Point&& other)
{
x = other.x;
y = other.y;
other.x = 0;
other.y = 0;
std::cout << "move assignment\n";
return *this;
}
void print() const
{
std::cout << x << ',' << y << '\n';
}
};
int main()
{
Point a{ 3, 5 };
Point b{ 9, 9 };
b = static_cast<Point&&>(a);
a.print();
b.print();
Point c{1, 1};
c = std::move(b);
std::cout << "Third: "; c.print();
std::cout << "Source b: "; b.print();
return 0;
}
Example output
move assignment
0,0
3,5
move assignment
Third: 3,5
Source b: 0,0
0 comments
No comments yet. Be the first.