Move Semantics & Rvalue References

Move semantics ও rvalue reference

Read: ~40 min 16 practice problems

1. The Problem Move Solves

Returning a 1GB std::vector from a function shouldn't require copying 1GB. Before C++11, it sometimes did. Move semantics let the new owner steal the underlying buffer in O(1) instead.

একটি বড় vector function থেকে return করার সময় copy করার দরকার নেই। তার পরিবর্তে নতুন object আগের object-এর data চুরি করে নিতে পারে — এটিই হলো move semantics।

2. lvalue vs rvalue

CategoryExampleHas identity?
lvaluex, arr[i], *pYes (named)
rvalue42, x + 1, foo()No (temporary)
  • T& binds to lvalues only
  • const T& binds to anything
  • T&& binds to rvalues — "you may steal from me"

3. Move Constructor & Assignment

move.cpp
#include <iostream>
#include <vector>
#include <string>

class Buffer {
    std::string data_;
public:
    Buffer(std::string s) : data_(std::move(s)) {}

    Buffer(const Buffer& o) : data_(o.data_) {
        std::cout << "copy\n";
    }
    Buffer(Buffer&& o) noexcept : data_(std::move(o.data_)) {
        std::cout << "move\n";
    }
};

int main() {
    Buffer a{"hello"};
    Buffer b = a;              // copy
    Buffer c = std::move(a);   // move
}

4. std::move — Just a Cast

std::move(x) doesn't actually move anything. It casts x to T&&, telling the compiler "treat this as an rvalue — feel free to steal." Whoever takes the move ref does the actual work.

After move The moved-from object is in an unspecified-but-valid state. Don't use it except to assign or destroy.

5. Why noexcept on Move?

STL containers (vector reallocation, etc.) only use move if the move is noexcept. Otherwise they copy for strong exception safety. Always mark your move ctor / move assign as noexcept.

6. Perfect Forwarding (Preview)

In templates, T&& is a "forwarding reference" — keeps lvalue/rvalue-ness:

forward.cpp
template<typename T>
void wrapper(T&& arg) {
    target(std::forward<T>(arg)); // preserves value category
}

7. Practice Problems

  1. Lvalue or rvalue? x + 1
    ✨ Show Answer

    rvalue (temporary).

  2. Lvalue or rvalue? arr[5]
    ✨ Show Answer

    lvalue.

  3. What does std::move(x) actually do?
    ✨ Show Answer

    Casts x to T&&. Just a static_cast. No data is moved.

  4. Why must move ops be noexcept for vector to use them?
    ✨ Show Answer

    Vector's reallocation provides strong exception safety. If a move can throw, vector must use copy instead so a partial reallocation doesn't lose elements.

  5. Write a non-throwing move constructor.
    ✨ Show Answer
    X(X&& o) noexcept : data_(std::move(o.data_)) {}
  6. After auto y = std::move(x), what's safe to do with x?
    ✨ Show Answer

    Assign a new value, destroy it. Don't read meaningful data — it's in an unspecified state.

  7. Why is const T&& rare?
    ✨ Show Answer

    You can't move from a const object — there's nothing to steal. So const rvalue refs are mostly useless.

  8. Pass a temp string to a function: copy or move?
    ✨ Show Answer

    If the parameter is std::string s (by value), the temp is moved into s for free.

  9. Why use data_(std::move(s)) in a constructor body?
    ✨ Show Answer

    The parameter s is an lvalue inside the function. To enable move construction of data_, cast s to rvalue with std::move.

  10. Will the compiler auto-generate move ops?
    ✨ Show Answer

    Only if you haven't declared destructor, copy ctor, copy assign, or move assign. Otherwise, the move ops are suppressed and you must write or default them.

  11. Force defaulted move ctor.
    ✨ Show Answer
    X(X&&) noexcept = default;
  12. Why do unique_ptr move but not copy?
    ✨ Show Answer

    Sole-ownership invariant: only one owner. Copying would mean two owners → double-delete. Move transfers ownership.

  13. Show that returning a local string is moved.
    ✨ Show Answer
    std::string make() { std::string s = "big string"; return s; }
    // no copy: NRVO or implicit move
  14. What is RVO / NRVO?
    ✨ Show Answer

    Return-Value Optimization. Compiler constructs the return value directly in the caller's space — no move, no copy. C++17 makes RVO mandatory in many cases.

  15. When NOT to mark move noexcept?
    ✨ Show Answer

    If your move can actually throw. But ideally design so it can't (move pointers/handles, leave the source empty).

  16. Is std::move(x) the same as moving x?
    ✨ Show Answer

    No. It just enables moving. The actual move happens when something binds the rvalue ref and steals from it.

Summary

Move semantics let big objects transfer ownership in O(1) instead of copying. T&& is an rvalue reference. std::move is a cast that enables moves; the actual work happens in the move ctor / assign. Always mark moves noexcept. After move, source is in an unspecified-but-valid state.

Next Module → Smart Pointers Deep Dive.