1/*
2 * Move Semantics and Value Categories Demo
3 *
4 * Demonstrates:
5 * - lvalue vs rvalue
6 * - Move constructor and move assignment
7 * - std::move and std::forward
8 * - Perfect forwarding
9 * - Rule of five / rule of zero
10 * - Return value optimization (RVO)
11 *
12 * Compile: g++ -std=c++20 -Wall -Wextra 07_move_semantics.cpp -o move_semantics
13 */
14
15#include <iostream>
16#include <vector>
17#include <string>
18#include <utility>
19#include <algorithm>
20
21// ============ Value Categories ============
22void demo_value_categories() {
23 std::cout << "\n=== Value Categories ===\n";
24
25 int x = 10; // x is an lvalue
26 int y = 20; // y is an lvalue
27
28 int z = x + y; // x + y is an rvalue (temporary)
29
30 std::cout << "lvalue x = " << x << "\n";
31 std::cout << "lvalue y = " << y << "\n";
32 std::cout << "rvalue (x + y) stored in z = " << z << "\n";
33
34 // int& ref1 = x + y; // Error: can't bind lvalue ref to rvalue
35 int&& ref2 = x + y; // OK: rvalue reference
36 std::cout << "rvalue reference ref2 = " << ref2 << "\n";
37
38 const int& ref3 = x + y; // OK: const lvalue ref can bind to rvalue
39 std::cout << "const lvalue reference ref3 = " << ref3 << "\n";
40}
41
42// ============ Rule of Five ============
43class Buffer {
44private:
45 size_t size_;
46 int* data_;
47 std::string name_;
48
49public:
50 // Constructor
51 Buffer(size_t size, const std::string& name)
52 : size_(size), data_(new int[size]), name_(name) {
53 std::cout << " [Constructor: " << name_ << ", size=" << size_ << "]\n";
54 for (size_t i = 0; i < size_; i++) {
55 data_[i] = i;
56 }
57 }
58
59 // Destructor
60 ~Buffer() {
61 std::cout << " [Destructor: " << name_ << "]\n";
62 delete[] data_;
63 }
64
65 // Copy constructor
66 Buffer(const Buffer& other)
67 : size_(other.size_), data_(new int[other.size_]),
68 name_(other.name_ + "_copy") {
69 std::cout << " [Copy constructor: " << name_ << "]\n";
70 std::copy(other.data_, other.data_ + size_, data_);
71 }
72
73 // Copy assignment
74 Buffer& operator=(const Buffer& other) {
75 std::cout << " [Copy assignment: " << other.name_ << " -> " << name_ << "]\n";
76 if (this != &other) {
77 delete[] data_;
78 size_ = other.size_;
79 data_ = new int[size_];
80 std::copy(other.data_, other.data_ + size_, data_);
81 }
82 return *this;
83 }
84
85 // Move constructor
86 Buffer(Buffer&& other) noexcept
87 : size_(other.size_), data_(other.data_),
88 name_(std::move(other.name_) + "_moved") {
89 std::cout << " [Move constructor: " << name_ << "]\n";
90 other.size_ = 0;
91 other.data_ = nullptr;
92 }
93
94 // Move assignment
95 Buffer& operator=(Buffer&& other) noexcept {
96 std::cout << " [Move assignment: " << other.name_ << " -> " << name_ << "]\n";
97 if (this != &other) {
98 delete[] data_;
99 size_ = other.size_;
100 data_ = other.data_;
101 name_ = std::move(other.name_) + "_moved";
102
103 other.size_ = 0;
104 other.data_ = nullptr;
105 }
106 return *this;
107 }
108
109 void print() const {
110 std::cout << " Buffer '" << name_ << "' [size=" << size_ << "]: ";
111 if (data_) {
112 for (size_t i = 0; i < std::min(size_, size_t(5)); i++) {
113 std::cout << data_[i] << " ";
114 }
115 if (size_ > 5) std::cout << "...";
116 } else {
117 std::cout << "(empty)";
118 }
119 std::cout << "\n";
120 }
121};
122
123void demo_rule_of_five() {
124 std::cout << "\n=== Rule of Five ===\n";
125
126 Buffer buf1(10, "buf1");
127 buf1.print();
128
129 // Copy constructor
130 Buffer buf2 = buf1;
131 buf2.print();
132
133 // Move constructor
134 Buffer buf3 = std::move(buf1);
135 buf3.print();
136 buf1.print(); // buf1 is now empty
137
138 // Copy assignment
139 Buffer buf4(5, "buf4");
140 buf4 = buf2;
141 buf4.print();
142
143 // Move assignment
144 Buffer buf5(3, "buf5");
145 buf5 = std::move(buf3);
146 buf5.print();
147}
148
149// ============ std::move ============
150void demo_std_move() {
151 std::cout << "\n=== std::move ===\n";
152
153 std::string str1 = "Hello, World!";
154 std::cout << "str1 before move: '" << str1 << "'\n";
155
156 std::string str2 = std::move(str1); // str1 is moved from
157 std::cout << "str1 after move: '" << str1 << "'\n";
158 std::cout << "str2 after move: '" << str2 << "'\n";
159
160 // Move into vector
161 std::vector<std::string> vec;
162 std::string str3 = "Move me!";
163 vec.push_back(std::move(str3));
164 std::cout << "str3 after push_back(move): '" << str3 << "'\n";
165 std::cout << "vec[0]: '" << vec[0] << "'\n";
166}
167
168// ============ Perfect Forwarding ============
169void process_lvalue(int& x) {
170 std::cout << " Processing lvalue: " << x << "\n";
171}
172
173void process_rvalue(int&& x) {
174 std::cout << " Processing rvalue: " << x << "\n";
175}
176
177// Universal reference with perfect forwarding
178template<typename T>
179void forward_to_process(T&& arg) {
180 if constexpr (std::is_lvalue_reference_v<T>) {
181 process_lvalue(arg);
182 } else {
183 process_rvalue(std::forward<T>(arg));
184 }
185}
186
187void demo_perfect_forwarding() {
188 std::cout << "\n=== Perfect Forwarding ===\n";
189
190 int x = 42;
191
192 forward_to_process(x); // lvalue
193 forward_to_process(100); // rvalue
194 forward_to_process(x + 10); // rvalue
195}
196
197// ============ Rule of Zero ============
198class SimpleBuffer {
199private:
200 std::vector<int> data_; // STL handles memory
201 std::string name_;
202
203public:
204 SimpleBuffer(size_t size, const std::string& name)
205 : data_(size), name_(name) {
206 std::cout << " [SimpleBuffer constructor: " << name_ << "]\n";
207 for (size_t i = 0; i < size; i++) {
208 data_[i] = i;
209 }
210 }
211
212 // No destructor, copy/move constructors, or assignment operators needed!
213 // Compiler-generated versions work correctly
214
215 void print() const {
216 std::cout << " SimpleBuffer '" << name_ << "' [size=" << data_.size() << "]\n";
217 }
218};
219
220void demo_rule_of_zero() {
221 std::cout << "\n=== Rule of Zero ===\n";
222
223 SimpleBuffer buf1(10, "simple1");
224 buf1.print();
225
226 // Copy (compiler-generated)
227 SimpleBuffer buf2 = buf1;
228 buf2.print();
229
230 // Move (compiler-generated)
231 SimpleBuffer buf3 = std::move(buf1);
232 buf3.print();
233
234 std::cout << " No manual memory management needed!\n";
235}
236
237// ============ Return Value Optimization (RVO) ============
238Buffer create_buffer(size_t size, const std::string& name) {
239 return Buffer(size, name); // RVO: no move/copy
240}
241
242void demo_rvo() {
243 std::cout << "\n=== Return Value Optimization (RVO) ===\n";
244
245 std::cout << "Creating buffer via function...\n";
246 Buffer buf = create_buffer(5, "rvo_buffer");
247 buf.print();
248 std::cout << "Note: No move/copy constructor called (RVO)!\n";
249}
250
251// ============ Move-Only Types ============
252class MoveOnly {
253private:
254 std::unique_ptr<int> data_;
255 std::string name_;
256
257public:
258 MoveOnly(int value, const std::string& name)
259 : data_(std::make_unique<int>(value)), name_(name) {
260 std::cout << " [MoveOnly constructor: " << name_ << "]\n";
261 }
262
263 // Delete copy
264 MoveOnly(const MoveOnly&) = delete;
265 MoveOnly& operator=(const MoveOnly&) = delete;
266
267 // Default move
268 MoveOnly(MoveOnly&&) = default;
269 MoveOnly& operator=(MoveOnly&&) = default;
270
271 void print() const {
272 std::cout << " MoveOnly '" << name_ << "': ";
273 if (data_) {
274 std::cout << *data_;
275 } else {
276 std::cout << "(empty)";
277 }
278 std::cout << "\n";
279 }
280};
281
282void demo_move_only() {
283 std::cout << "\n=== Move-Only Types ===\n";
284
285 MoveOnly obj1(42, "obj1");
286 obj1.print();
287
288 // MoveOnly obj2 = obj1; // Error: copy deleted
289 MoveOnly obj2 = std::move(obj1); // OK: move
290 obj2.print();
291 obj1.print(); // obj1 is now empty
292
293 // Store in vector
294 std::vector<MoveOnly> vec;
295 vec.push_back(std::move(obj2));
296 std::cout << " Moved into vector\n";
297}
298
299// ============ Main ============
300int main() {
301 std::cout << "Move Semantics and Value Categories Demo\n";
302 std::cout << "==========================================\n";
303
304 demo_value_categories();
305 demo_rule_of_five();
306 demo_std_move();
307 demo_perfect_forwarding();
308 demo_rule_of_zero();
309 demo_rvo();
310 demo_move_only();
311
312 std::cout << "\nAll demos completed!\n";
313 return 0;
314}