std::move(a) neither moves bytes nor guarantees that a becomes empty. It enables an operation that may transfer ownership. Tracing 7, 11, 13, 17 through a runnable Buffer shows why noexcept affects containers. The Coding and Skill Development courses connect this ownership model to broader programming practice.
The ownership problem that move semantics solves
Buffer a{7, 11, 13, 17} owns four heap integers. Copying needs another allocation and four copied values. Moving can transfer the pointer and size, leaving a safe to destroy or assign.
Every live block must have one owner and one deletion. A moved-from object must remain valid, but its value may be unspecified. Our class deliberately makes it empty: size_ == 0 and data_ == nullptr.
A deep copy handles n values; this move transfers a pointer and size.

lvalues, rvalues and what std::move does
Named objects such as word are lvalues; temporaries such as std::string{"move"} are rvalues. T& binds to a modifiable lvalue, const T& to either category, and T&& commonly marks a consuming overload.
#include <iostream>
#include <string>
#include <utility>
void tag(const std::string& text) {
std::cout << "lvalue overload: " << text << '\n';
}
void tag(std::string&& text) {
std::cout << "rvalue overload: " << text << '\n';
}
int main() {
std::string word = "move";
tag(word);
tag(std::move(word));
std::cout << "after tag: " << word << '\n';
}The exact output is:
lvalue overload: move
rvalue overload: move
after tag: movestd::move is a cast that permits rvalue-overload selection. This overload only reads, so word stays unchanged. A named rvalue reference is an lvalue expression: tag(alias) selects const std::string&, while tag(std::move(alias)) selects std::string&&. Functions in C reviews parameter passing; C has no move constructors or rvalue references.
Worked example: a Buffer that copies or transfers
Save this as MoveSemanticsDemo.cpp:
#include <algorithm>
#include <cstddef>
#include <initializer_list>
#include <iostream>
#include <utility>
class Buffer {
std::size_t size_{0};
int* data_{nullptr};
public:
Buffer(std::initializer_list<int> values)
: size_(values.size()),
data_(size_ == 0 ? nullptr : new int[size_]) {
if (size_ != 0) {
std::copy(values.begin(), values.end(), data_);
}
}
~Buffer() {
delete[] data_;
}
Buffer(const Buffer& other)
: size_(other.size_),
data_(size_ == 0 ? nullptr : new int[size_]) {
if (size_ != 0) {
std::copy(other.data_, other.data_ + size_, data_);
}
std::cout << "copy constructor\n";
}
Buffer(Buffer&& other) noexcept
: size_(std::exchange(other.size_, 0)),
data_(std::exchange(other.data_, nullptr)) {
std::cout << "move constructor\n";
}
Buffer& operator=(const Buffer& other) {
if (this != &other) {
Buffer temporary(other);
swap(temporary);
}
return *this;
}
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data_;
size_ = std::exchange(other.size_, 0);
data_ = std::exchange(other.data_, nullptr);
}
return *this;
}
void swap(Buffer& other) noexcept {
std::swap(size_, other.size_);
std::swap(data_, other.data_);
}
std::size_t size() const {
return size_;
}
int& operator[](std::size_t index) {
return data_[index];
}
void print(const char* name) const {
std::cout << name << "=[";
for (std::size_t i = 0; i < size_; ++i) {
if (i != 0) {
std::cout << ", ";
}
std::cout << data_[i];
}
std::cout << "]\n";
}
};
int main() {
Buffer a{7, 11, 13, 17};
Buffer b = a;
b[0] = 70;
Buffer c = std::move(a);
std::cout << "a.size=" << a.size() << '\n';
b.print("b");
c.print("c");
}Run g++ -std=c++17 -Wall -Wextra -pedantic MoveSemanticsDemo.cpp && ./a.out. The output is:
copy constructor
move constructor
a.size=0
b=[70, 11, 13, 17]
c=[7, 11, 13, 17]The copy gives b independent storage, so b[0] = 70 cannot alter Block A. The move uses std::exchange to take both members and install {0, nullptr} in a; delete[] nullptr is safe.

Pointers in C for GATE reviews heap-address diagrams. A pointer is only an address; the Buffer contract assigns ownership and deletion.
Rule of Zero, Rule of Five and move-only ownership
Because Buffer owns raw storage, it defines all five special operations. A shallow copy would make two objects delete one array. Its copy assignment replaces through a temporary; move assignment guards self-move, releases, then steals.
Prefer the Rule of Zero in application code:
struct Record { std::string name; std::vector<int> marks; };
Record r{"Asha", {72, 81, 94}};These members manage themselves. Special-member declarations can suppress implicit moves; std::move cannot create one.
For sole ownership, auto p = std::make_unique<int>(42); auto q = std::move(p); leaves p == nullptr and *q == 42; copying is disabled.
Return values, copy elision and noexcept
Under C++17, Buffer make_primes() { return Buffer{2, 3, 5}; } can initialise its destination directly. For a named local, return result; may use named return value optimisation, with move as fallback. return std::move(result); can block that optimisation.
Suppose a vector<Packet> reserves one slot, inserts 10, then 20. With a noexcept move, growth relocates 10 once, printing move 10. If move may throw and copy exists, it may print copy 10 to preserve its exception guarantee. A false noexcept can cause termination. Buffer only exchanges a size and pointer, so its promise is truthful.
Common move-semantics mistakes and repairs
Mistake | What goes wrong | Repair |
|---|---|---|
Assuming | A cast transfers nothing | Check the overload and whether it consumes |
Reading an undocumented moved-from value | It may be unspecified | Destroy, assign, or use documented operations |
Not resetting a source pointer | Two destructors may delete one block | Exchange with |
Moving from |
| Expect an available const copy overload |
Writing | It can block named-return optimisation | Write |
Omitting a destructive self-move guard | An object may release its resource | Check |
Marking a throwing move | An exception can terminate | Use it only when true |
For const Buffer fixed{5, 8}; Buffer next = std::move(fixed);, this class copies because Buffer&& cannot bind to const. Both objects contain [5, 8]. After our earlier move, a.size() is 0, so a[0] is invalid.
How assessments test moves, with checkable exercises
Questions ask you to select overloads, trace constructors, find double deletion, or explain a container copy.
Classify the expression, select an overload, decide whether it consumes, record both states, then count owners. Here c owns [7, 11, 13, 17], a.size() is 0, and Block A is deleted once.
Add
Buffer d = b; d[1] = 110;. Answer:bstays[70, 11, 13, 17];dis[70, 110, 13, 17].Add
Buffer e = std::move(c);. Answer:c.size()is0;eholds[7, 11, 13, 17].Predict
const Buffer fixed{5, 8}; Buffer next = std::move(fixed);. Answer:copy constructor.Omit the source reset in a hand-written move. Answer: both objects retain one pointer and may delete it. Reset pointer and size.
Practise constructor traces and ownership questions in the Coding for Placements course.
The short version and the next step
A move transfers only when the selected operation implements transfer.
std::moveis an rvalue cast.Moved-from objects remain valid, but their value may be unspecified.
Resource owners must prevent shared deletion.
Truthful
noexceptcan let containers relocate by move.Prefer Rule-of-Zero types when library members can own resources.
b owns [70, 11, 13, 17]; c owns the original [7, 11, 13, 17]; a is empty by this class's contract. Continue with the C++ Programming course for structured practice.




