Di Chapter Ownership dan Pemilihan Smart Pointer kita sudah dua kali menulis std::move(...) — sekarang saatnya membuka mekanismenya. Move semantics adalah cara C++ memindahkan resource dari satu variabel ke variabel lain tanpa menyalin isinya: alamat heap tidak diganti, data tidak digandakan; yang berpindah hanyalah kepemilikan, dan sumbernya ditinggalkan dalam keadaan kosong yang sah. Untuk std::unique_ptr yang kekuatannya justru pada larangan menyalin, move adalah satu-satunya pintu perpindahan. Chapter ini memakai unique_ptr untuk menunjukkan tiga arah perpindahan — antar variabel, keluar fungsi, dan masuk container — sebelum detail penuhnya dibedah pada Section Move Semantics dan Value Categories.

std::move: Tanda “Silakan Pindahkan” #

std::move sendiri tidak memindahkan apa pun; ia menandai sebuah ekspresi sebagai “boleh dipindah” sehingga compiler memilih operasi pemindahan alih-alih penyalinan. Saat penanda ini bertemu unique_ptr, hasilnya sederhana dan terukur: alamat yang disimpan sumber dipindahkan ke tujuan, dan sumber menjadi nullptr. Inilah pola yang membuat kepemilikan bisa berpindah tanpa membuka celah dua pemilik — perpindahan dan pengosongan terjadi sebagai satu kesatuan atomik.

cpp
#include <iostream>
#include <memory>
#include <utility>
#include <vector>

std::unique_ptr<int> buatNilai(int v) {
    return std::make_unique<int>(v);   // berpindah keluar tanpa disalin
}

int main() {
    auto a = std::make_unique<int>(10);
    std::unique_ptr<int> b = std::move(a);   // kepemilikan pindah: a -> b

    std::cout << "b berisi : " << *b << "\n";
    std::cout << "a kosong? " << (a == nullptr ? "ya" : "tidak") << "\n";

    auto c = buatNilai(99);                  // berpindah keluar dari fungsi
    std::cout << "c berisi : " << *c << "\n";

    std::vector<std::unique_ptr<int>> gudang;
    gudang.push_back(std::move(b));          // berpindah masuk ke vector
    gudang.push_back(std::move(c));
    std::cout << "gudang   : " << *gudang[0] << " dan " << *gudang[1] << "\n";

    return 0;
}

Output yang Dihasilkan:

text
b berisi : 10
a kosong? ya
c berisi : 99
gudang   : 10 dan 99

Tiga arah perpindahan terbaca dari program di atas:

  1. Baris 12 (std::move(a)): kepemilikan pindah antar variabel — b kini memegang alamat heap berisi 10, dan a terbukti kosong pada output a kosong? ya.
  2. Baris 7 (return std::make_unique<int>(v);): kepemilikan berpindah keluar dari fungsi secara alami; memanggil buatNilai(99) menghasilkan c yang sah tanpa ekspresi move eksplisit di pemanggil.
  3. Baris 21-22 (push_back(std::move(...))): vector<unique_ptr<int>> hampir mustahil dibangun tanpa move — penyalinan dilarang, sehingga setiap elemen harus dipindahkan masuk.
  4. Baris 23 (*gudang[0]): setelah pindah, objek tetap satu-satunya objek yang sama — tidak ada penggandaan data di sepanjang perjalanannya.

Mengapa Move Layak Ada #

Motif besarnya adalah menghindari penyalinan yang mahal. std::string dengan isi satu paragraf, std::vector dengan jutaan elemen, atau buffer gambar — menyalinnya berarti memesan memori baru dan menggandakan byte demi byte, sedangkan memindahkannya hanya memindahkan tiga penunjuk internal. Dengan move semantics, container seperti std::vector bisa tumbuh tanpa menggandakan setiap elemen, std::string bisa dikembalikan dari fungsi tanpa salinan, dan unique_ptr bisa disimpan di mana saja. Semua itu tanpa biaya tambahan — resource yang sama, hanya berpindah pemilik.

Ada satu keadaan yang wajib dipahami sejak sekarang: moved-from state. Variabel yang sudah dipindahkan sah dan aman diakses — a == nullptr di atas adalah perbandingan yang sah — tetapi isinya tidak lagi menjanjikan apa pun kecuali nilai default tipe itu. Aturan praktisnya: jangan membaca nilai dari variabel yang sudah dipindahkan; nol-kan pemahaman kita bahwa ia kosong, lalu biarkan ia berakhir atau isi ulang.

Move yang kita gunakan di sini baru permukaannya — versi lengkapnya melibatkan rvalue reference, move constructor, dan move assignment yang dibedah tuntas pada Section Move Semantics dan Value Categories. Untuk kebutuhan manajemen memori, yang kita miliki sudah cukup: kepemilikan bisa berpindah, aman, dan eksplisit. Pertanyaan terakhir yang tersisa: apa yang terjadi pada resource itu bila exception menyela di tengah jalan? Itulah topik chapter berikutnya.

Referensi: cppreference — std::move , cppreference — std::unique_ptr .