Before I start writing code that uses rvalue references and move semantics I want to make sure that I fully understand it.
I read all of the pages from the following link:
http://thbecker.net/articles/rvalue_references/section_01.html
which was recommended in Nicolai M. Josuttis's book "The C++ Standard Library: A Tutorial and Reference"
And the entire Chapter 5. Rvalue References, Move Semantics, and Perfect Forwarding from "Effective Modern C++" by Scott Meyers.
A wrote this little example to help me understand.
May you please look over my code and tell my whether I'm using the Standard Library facilities correctly?
Don't get overwhelmed by the number of lines of code, most of it is just print statements.
Basically what this code does is type
Container
is composed of an object of type
A
. The point of type
Container
is to move an object of type
A
(into)
Container
's data member and to move (out of)
Container
's data member into another variable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
|
#include <iostream>
#include <utility>
class A
{
public:
A(int arg) : data{arg} { std::cout << "A::A()" << '\n'; }
~A() { std::cout << "A::~A(), data == " << data << '\n'; }
A(const A& other) { std::cout << "A::A(const A&)" << '\n'; data = other.data; }
A(A&& other) { std::cout << "A::A(A&&)" << '\n'; data = other.data; other.data = 0; }
A& operator=(const A& other) { std::cout << "A& A::operator=(const A&)" << '\n'; data = other.data; return *this; }
A& operator=(A&& other) { std::cout << "A& A::operator=(A&& other)" << '\n'; data = other.data; other.data = 0; return *this; }
int data;
};
class Container
{
public:
A&& release_ownership()
{
std::cout << "Entering Container::release_ownership()" << '\n';
obj.data = 10;
std::cout << "Container::obj.data == " << obj.data << '\n';
std::cout << "Exiting Container::release_ownership()" << '\n';
return std::move(obj);
}
void acquire_ownership(A&& arg)
{
std::cout << "Entering Container::acquire_ownership()" << '\n';
obj = std::move(arg);
std::cout << "Exiting Container::acquire_ownership()" << '\n';
}
A obj{5};
};
int main()
{
std::cout << "Entering main()" << '\n';
std::cout << "Before declaring Container c;" << '\n';
Container c;
std::cout << "After declaring Container c;" << '\n';
std::cout << "Before calling c.release_ownership()" << '\n';
A local1{std::move(c.release_ownership())};
std::cout << "After calling c.release_ownership()" << '\n';
std::cout << "local1.data == " << local1.data << '\n';
std::cout << "c.obj.data == " << c.obj.data << '\n';
std::cout << "\n\n";
std::cout << "Before declaring A local2;" << '\n';
A local2(3);
std::cout << "After declaring A local2;" << '\n';
std::cout << "local2.data == " << local2.data << '\n';
std::cout << "Before calling c.acquire_ownership()" << '\n';
c.acquire_ownership(std::move(local2));
std::cout << "After calling c.acquire_ownership()" << '\n';
std::cout << "local2.data == " << local2.data << '\n';
std::cout << "c.obj.data == " << c.obj.data << '\n';
std::cout << "Exiting main()" << '\n';
return 0;
}
|
Output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
Entering main()
Before declaring Container c;
A::A()
After declaring Container c;
Before calling c.release_ownership()
Entering Container::release_ownership()
Container::obj.data == 10
Exiting Container::release_ownership()
A::A(A&&)
After calling c.release_ownership()
local1.data == 10
c.obj.data == 0
Before declaring A local2;
A::A()
After declaring A local2;
local2.data == 3
Before calling c.acquire_ownership()
Entering Container::acquire_ownership()
A& A::operator=(A&& other)
Exiting Container::acquire_ownership()
After calling c.acquire_ownership()
local2.data == 0
c.obj.data == 3
Exiting main()
A::~A(), data == 0
A::~A(), data == 10
A::~A(), data == 3
|