☀️Siang
Python

🚀 Modern C++ (C++17/20): Fitur Terbaru

Tutorial lengkap belajar Modern C++ — smart pointers, move semantics, constexpr, concepts, ranges, dan fitur C++17/20 terbaru dengan contoh kode praktis

Artikel: Cpp Modern Artikel: Cpp Modern


1. Pengenalan Modern C++

Modern C++ merujuk pada praktik dan fitur C++ sejak C++11 hingga C++20 (dan seterusnya). Setiap standar baru membawa fitur yang membuat C++ lebih aman, lebih ekspresif, dan lebih mudah digunakan tanpa mengorbankan performa.

Philosophy utama Modern C++ adalah: "Don't pay for what you don't use" — fitur baru harus zero-cost abstraction jika tidak digunakan, tapi memberikan kemudahan luar biasa saat dipakai.

Evolusi Standar C++

Standar Tahun Fitur Utama
C++112011auto, lambda, move semantics, smart pointers, range-for
C++142014Generic lambda, return type deduction, variable template
C++172017Structured bindings, optional, variant, if constexpr, filesystem
C++202020Concepts, ranges, coroutines, modules, spaceship operator
C++232023std::expected, std::print, deducing this, flat_map
Diagram: Timeline Modern C++
C++11
auto
lambda
move
smart
ptr
TIMELINE MODERN C++
2011 2014 2017 2020 2023
▼ ▼ ▼ ▼ ▼
C++14 C++17 C++20 C++23
Generic optional concepts expected
lambda variant ranges print
return struct. coroutines deducing
type bindings modules this
deduct. if const. spaceship flat_map
← Revolusi → Iterasi → Maturity → Refinement →

Compile Modern C++

Terminal
# Kompilasi dengan standar C++ tertentu
g++ -std=c++17 -O2 -Wall -Wextra program.cpp -o program
g++ -std=c++20 -O2 -Wall -Wextra program.cpp -o program
g++ -std=c++23 -O2 -Wall -Wextra program.cpp -o program

# Clang
clang++ -std=c++20 -O2 program.cpp -o program

# MSVC (Visual Studio)
cl /std:c++20 /EHsc program.cpp


2. Smart Pointers

Smart pointers mengelola alokasi memori secara otomatis — memori dibebaskan saat smart pointer keluar scope. Ini menghilangkan kebutuhan untuk manual delete dan mencegah memory leak.

Jenis Smart Pointers

Jenis Kepemilikan Penggunaan
unique_ptrEksklusif (satu pemilik)Paling umum, default choice
shared_ptrDibagi (reference counting)Banyak pemilik, dealokasi saat ref count = 0
weak_ptrTidak memiliki (observer)Mencegah circular reference
C++ — Smart Pointers
#include <iostream>
#include <memory>
#include <string>
#include <vector>
using namespace std;

class Kendaraan {
private:
    string merek;
    int tahun;

public:
    Kendaraan(string m, int t) : merek(move(m)), tahun(t) {
        cout << "Kendaraan " << merek << " dibuat" << endl;
    }

    ~Kendaraan() {
        cout << "Kendaraan " << merek << " dihapus" << endl;
    }

    void info() const {
        cout << merek << " (" << tahun << ")" << endl;
    }
};

int main() {
    // ===== unique_ptr — single ownership =====
    cout << "=== unique_ptr ===" << endl;
    {
        auto mobil = make_unique<Kendaraan>("Toyota", 2024);
        mobil->info();

        // Tidak bisa di-copy
        // auto mobil2 = mobil;  // ERROR!

        // Bisa di-move
        auto mobil2 = move(mobil);
        // mobil sekarang nullptr
        if (!mobil) cout << "mobil sudah dipindahkan" << endl;
        mobil2->info();

        // Array of unique_ptr
        vector<unique_ptr<Kendaraan>> garasi;
        garasi.push_back(make_unique<Kendaraan>("Honda", 2023));
        garasi.push_back(make_unique<Kendaraan>("BMW", 2025));
    } // unique_ptr otomatis dealokasi di sini

    cout << "\n=== shared_ptr ===" << endl;
    {
        auto motor = make_shared<Kendaraan>("Yamaha", 2024);
        cout << "Ref count: " << motor.use_count() << endl;  // 1

        {
            auto motor2 = motor;  // Copy — ref count bertambah
            cout << "Ref count: " << motor.use_count() << endl;  // 2
            motor2->info();
        } // motor2 keluar scope — ref count berkurang

        cout << "Ref count: " << motor.use_count() << endl;  // 1
    } // Ref count = 0, object dihapus

    cout << "\n=== weak_ptr ===" << endl;
    weak_ptr<Kendaraan> weakRef;
    {
        auto sp = make_shared<Kendaraan>("Suzuki", 2024);
        weakRef = sp;
        cout << "Ref count: " << sp.use_count() << endl;  // 1 (weak_ptr tidak dihitung!)

        if (auto locked = weakRef.lock()) {
            locked->info();  // Bisa diakses selama object masih hidup
        }
    } // sp keluar scope — object dihapus

    // weak_ref sekarang expired
    if (weakRef.expired()) {
        cout << "Object sudah dihapus (expired)" << endl;
    }

    return 0;
}
💡 Best Practice Smart Pointers

Gunakan unique_ptr sebagai default. Gunakan shared_ptr hanya ketika benar-benar ada beberapa pemilik. Gunakan weak_ptr untuk memecah circular reference. Jangan pernah campur new/delete dengan smart pointers!



3. Move Semantics

Move semantics (C++11) memungkinkan transfer kepemilikan resource tanpa menyalin data. Ini sangat meningkatkan performa saat bekerja dengan objek besar sementara (temporary objects).

Lvalue dan Rvalue

C++ — Move Semantics
#include <iostream>
#include <string>
#include <vector>
#include <utility>
using namespace std;

class Buffer {
private:
    int* data;
    size_t ukuran;
    string nama;

public:
    // Constructor
    Buffer(const string& n, size_t uk)
        : nama(n), ukuran(uk), data(new int[uk]) {
        cout << "Constructor: " << nama << " (" << ukuran << elemen)" << endl;
    }

    // Copy constructor — DEEP COPY (lambat untuk data besar)
    Buffer(const Buffer& other)
        : nama(other.nama + "_copy"), ukuran(other.ukuran),
          data(new int[other.ukuran]) {
        for (size_t i = 0; i < ukuran; i++) data[i] = other.data[i];
        cout << "Copy constructor: " << nama << endl;
    }

    // Move constructor — TRANSFER OWNERSHIP (sangat cepat!)
    Buffer(Buffer&& other) noexcept
        : data(other.data), ukuran(other.ukuran), nama(move(other.nama)) {
        other.data = nullptr;  // Kosongkan sumber
        other.ukuran = 0;
        cout << "Move constructor: " << nama << endl;
    }

    // Copy assignment
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {
            delete[] data;
            ukuran = other.ukuran;
            nama = other.nama + "_assigned";
            data = new int[ukuran];
            for (size_t i = 0; i < ukuran; i++) data[i] = other.data[i];
        }
        return *this;
    }

    // Move assignment
    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            ukuran = other.ukuran;
            nama = move(other.nama);
            other.data = nullptr;
            other.ukuran = 0;
        }
        return *this;
    }

    ~Buffer() {
        cout << "Destructor: " << nama << endl;
        delete[] data;
    }

    void info() const {
        cout << nama << " - " << ukuran << " elemen" << endl;
    }
};

Buffer buatBuffer() {
    Buffer temp("temp", 1000);
    return temp;  // NRVO atau move
}

int main() {
    // Copy vs Move
    Buffer a("buffer_a", 1000);
    Buffer b = a;             // Copy constructor (a tetap hidup)
    Buffer c = move(a);       // Move constructor (a sekarang kosong)

    a.info();  // Kosong/null
    c.info();  // Mengambil alih data

    // std::move untuk transfer
    string s1 = "Hello World";
    string s2 = move(s1);
    cout << "s1 setelah move: \"" << s1 << "\"" << endl;  // Kosong!
    cout << "s2: \"" << s2 << "\"" << endl;

    // Move untuk vector (avoid copy)
    vector<int> v1 = {1, 2, 3, 4, 5};
    vector<int> v2 = move(v1);  // v1 sekarang kosong
    cout << "v1.size(): " << v1.size() << endl;  // 0
    cout << "v2.size(): " << v2.size() << endl;  // 5

    return 0;
}


4. Auto dan Decltype

Keyword auto membiarkan compiler menentukan tipe data secara otomatis. Ini mengurangi boilerplate dan meningkatkan keterbacaan kode.

C++ — Auto & Decltype
#include <iostream>
#include <vector>
#include <map>
#include <string>
using namespace std;

int main() {
    // auto — compiler deduces type
    auto x = 42;              // int
    auto pi = 3.14;           // double
    auto name = string("Budi"); // string
    auto flag = true;         // bool

    // Sangat berguna untuk tipe panjang
    map<string, vector<int>> data;
    data["matematika"] = {90, 85, 78};

    // Tanpa auto — verbose!
    for (map<string, vector<int>>::iterator it = data.begin();
         it != data.end(); ++it) {
        cout << it->first << endl;
    }

    // Dengan auto — clean!
    for (const auto& [nama, nilai] : data) {
        cout << nama << ": ";
        for (auto n : nilai) cout << n << " ";
        cout << endl;
    }

    // decltype — mendapatkan tipe dari expression
    int a = 10;
    decltype(a) b = 20;  // b bertipe int
    decltype(a + b) c = a + b;  // c bertipe int

    // decltype(auto) — preserve reference
    int& ref = a;
    decltype(auto) d = ref;  // d adalah int&

    // auto dengan trailing return type
    auto tambah = [](auto a, auto b) { return a + b; };
    cout << tambah(3, 4) << endl;     // 7
    cout << tambah(3.5, 2.5) << endl; // 6.0

    return 0;
}


5. Lambda Expressions

Lambda adalah anonymous function yang bisa didefinisikan inline. Lambda sangat berguna untuk callback, algoritma STL, dan event handling.

C++ — Lambda
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <string>
using namespace std;

// Lambda syntax:
// [capture](parameters) -> return_type { body }

int main() {
    // Lambda sederhana
    auto sapa = []() { cout << "Halo dari lambda!" << endl; };
    sapa();

    // Lambda dengan parameter
    auto tambah = [](int a, int b) { return a + b; };
    cout << "3 + 4 = " << tambah(3, 4) << endl;

    // Generic lambda (C++14)
    auto kali = [](auto a, auto b) { return a * b; };
    cout << "3 * 4 = " << kali(3, 4) << endl;
    cout << "3.5 * 2.0 = " << kali(3.5, 2.0) << endl;

    // Capture variables
    int multiplier = 3;
    auto kali_n = [multiplier](int x) { return x * multiplier; };
    cout << "5 * 3 = " << kali_n(5) << endl;

    // Capture by reference
    int counter = 0;
    auto increment = [&counter]() { counter++; };
    increment();
    increment();
    increment();
    cout << "Counter: " << counter << endl;  // 3

    // Capture all by value [=] or by reference [&]
    int a = 10, b = 20;
    auto sum = [=]() { return a + b; };       // By value
    auto modify = [&]() { a++; b++; };        // By reference
    modify();
    cout << "a=" << a << ", b=" << b << endl;  // 11, 21

    // Mutable lambda (bisa ubah capture by value)
    int count = 0;
    auto counter_fn = [count]() mutable {
        count++;
        return count;
    };
    cout << counter_fn() << endl;  // 1
    cout << counter_fn() << endl;  // 2
    cout << "Original: " << count << endl;  // 0 (tidak berubah)

    // Lambda dengan STL algorithms
    vector<int> v = {5, 3, 8, 1, 9, 2, 7};

    // Sort descending
    sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

    // Filter dengan lambda
    vector<int> genap;
    copy_if(v.begin(), v.end(), back_inserter(genap),
            [](int x) { return x % 2 == 0; });

    cout << "Genap: ";
    for (auto x : genap) cout << x << " ";
    cout << endl;

    // Transform
    vector<string> nama = {"budi", "ani", "dedi"};
    transform(nama.begin(), nama.end(), nama.begin(),
              [](string s) {
                  s[0] = toupper(s[0]);
                  return s;
              });

    cout << "Nama: ";
    for (const auto& n : nama) cout << n << " ";
    cout << endl;

    // Immediately invoked lambda
    int hasil = [](int x) { return x * x; }(7);
    cout << "7^2 = " << hasil << endl;

    return 0;
}


6. Constexpr

constexpr memungkinkan komputasi dilakukan saat compile-time, menghasilkan kode yang lebih cepat karena hasil sudah diketahui sebelum program berjalan.

C++ — Constexpr
#include <iostream>
#include <array>
using namespace std;

// Constexpr function — dihitung saat compile-time jika argumen constexpr
constexpr int faktorial(int n) {
    if (n <= 1) return 1;
    return n * faktorial(n - 1);
}

// Constexpr function dengan loop (C++14+)
constexpr int fibonacci(int n) {
    if (n <= 1) return n;
    int a = 0, b = 1;
    for (int i = 2; i <= n; i++) {
        int temp = a + b;
        a = b;
        b = temp;
    }
    return b;
}

// Constexpr class
class Titik {
private:
    double x, y;

public:
    constexpr Titik(double x, double y) : x(x), y(y) {}

    constexpr double getX() const { return x; }
    constexpr double getY() const { return y; }

    constexpr double jarak() const {
        return x * x + y * y;  // Simplified (tanpa sqrt untuk constexpr)
    }
};

// Constexpr if (C++17)
template <typename T>
auto proses(T val) {
    if constexpr (is_integral_v<T>) {
        return val * 2;         // Hanya compile untuk integral
    } else {
        return val + 0.5;       // Hanya compile untuk non-integral
    }
}

int main() {
    // Constexpr variable — nilai ditentukan saat compile
    constexpr int N = 5;
    constexpr int fact5 = faktorial(N);
    cout << "5! = " << fact5 << endl;  // 120

    constexpr int fib10 = fibonacci(10);
    cout << "fib(10) = " << fib10 << endl;  // 55

    // Array ukuran constexpr
    constexpr int SIZE = 10;
    array<int, SIZE> arr;  // Ukuran array compile-time

    // Constexpr object
    constexpr Titik p(3.0, 4.0);
    constexpr double j = p.jarak();
    cout << "Jarak^2 dari (3,4): " << j << endl;  // 25

    // Constexpr if
    cout << proses(5) << endl;     // 10 (integral path)
    cout << proses(3.14) << endl;  // 3.64 (floating path)

    // Compile-time table generation
    constexpr auto kuadrat = []() constexpr {
        array<int, 10> result{};
        for (int i = 0; i < 10; i++) {
            result[i] = i * i;
        }
        return result;
    }();

    cout << "Tabel kuadrat: ";
    for (int x : kuadrat) cout << x << " ";
    cout << endl;

    return 0;
}


7. Structured Bindings (C++17)

Structured bindings memungkinkan unpack nilai dari struct, pair, tuple, atau array ke variabel terpisah secara elegan.

C++ — Structured Bindings
#include <iostream>
#include <tuple>
#include <map>
#include <string>
#include <array>
using namespace std;

struct Koordinat {
    double x, y, z;
};

tuple<string, int, double> getDataMahasiswa() {
    return {"Budi", 20, 3.75};
}

int main() {
    // Unpack struct
    Koordinat p = {1.0, 2.0, 3.0};
    auto [x, y, z] = p;
    cout << "x=" << x << ", y=" << y << ", z=" << z << endl;

    // Unpack tuple
    auto [nama, umur, ipk] = getDataMahasiswa();
    cout << nama << ", " << umur << ", IPK: " << ipk << endl;

    // Unpack pair
    pair<string, int> p1 = {"apel", 5};
    auto [buah, jumlah] = p1;
    cout << buah << ": " << jumlah << endl;

    // Unpack array
    array<int, 3> arr = {10, 20, 30};
    auto [a, b, c] = arr;
    cout << a << ", " << b << ", " << c << endl;

    // Structured bindings dengan map (sangat berguna!)
    map<string, int> skor = {{"Budi", 85}, {"Ani", 92}, {"Dedi", 78}};

    for (const auto& [nama, nilai] : skor) {
        cout << nama << ": " << nilai << endl;
    }

    return 0;
}


8. Optional dan Variant (C++17)

std::optional mewakili nilai yang mungkin ada atau tidak. std::variant adalah type-safe union yang bisa menyimpan salah satu dari beberapa tipe.

C++ — Optional & Variant
#include <iostream>
#include <optional>
#include <variant>
#include <string>
#include <vector>
using namespace std;

// Optional — untuk return value yang mungkin gagal
optional<int> cariIndeks(const vector<int>& v, int target) {
    for (size_t i = 0; i < v.size(); i++) {
        if (v[i] == target) return static_cast<int>(i);
    }
    return nullopt;  // Tidak ditemukan
}

optional<string> getUser(int id) {
    if (id == 1) return "Budi";
    if (id == 2) return "Ani";
    return nullopt;  // User tidak ditemukan
}

// Variant — type-safe union
using Nilai = variant<int, double, string>;

void cetakNilai(const Nilai& n) {
    visit([](const auto& val) {
        cout << val << endl;
    }, n);
}

int main() {
    // Optional
    vector<int> data = {10, 20, 30, 40, 50};

    auto idx = cariIndeks(data, 30);
    if (idx.has_value()) {
        cout << "Ditemukan di indeks: " << idx.value() << endl;
    }

    // Atau gunakan value_or (default)
    auto idx2 = cariIndeks(data, 99);
    cout << "Indeks: " << idx2.value_or(-1) << endl;  // -1

    // Optional chaining
    auto user = getUser(1);
    if (user) {
        cout << "User: " << *user << endl;
    }

    auto unknown = getUser(99);
    cout << "User: " << unknown.value_or("Guest") << endl;

    // Variant
    Nilai n1 = 42;
    Nilai n2 = 3.14;
    Nilai n3 = string("Hello");

    cout << "\nVariant values:" << endl;
    cetakNilai(n1);  // 42
    cetakNilai(n2);  // 3.14
    cetakNilai(n3);  // Hello

    // Cek tipe variant
    cout << "n1 index: " << n1.index() << endl;  // 0 (int)
    cout << "n2 index: " << n2.index() << endl;  // 1 (double)

    if (holds_alternative<int>(n1)) {
        cout << "n1 adalah int: " << get<int>(n1) << endl;
    }

    // Visit pattern
    visit([](const auto& val) {
        using T = decay_t<decltype(val)>;
        if constexpr (is_same_v<T, int>) {
            cout << "Int: " << val << endl;
        } else if constexpr (is_same_v<T, double>) {
            cout << "Double: " << val << endl;
        } else {
            cout << "String: " << val << endl;
        }
    }, n1);

    return 0;
}


9. Concepts (C++20)

Concepts memungkinkan menetapkan constraint pada template parameters, memberikan error message yang lebih jelas dan kode yang lebih terdokumentasi.

C++20 — Concepts
#include <iostream>
#include <concepts>
#include <string>
#include <vector>
#include <type_traits>
using namespace std;

// Mendefinisikan concept sendiri
template <typename T>
concept Numeric = is_arithmetic_v<T>;

template <typename T>
concept Printable = requires(ostream& os, T val) {
    { os << val } -> same_as<ostream&>;
};

template <typename T>
concept HasSize = requires(T t) {
    { t.size() } -> convertible_to<size_t>;
};

template <typename T>
concept Container = HasSize<T> && requires(T t) {
    { t.begin() };
    { t.end() };
};

// Menggunakan concept sebagai constraint
template <Numeric T>
T kuadrat(T x) {
    return x * x;
}

// Atau dengan requires clause
template <typename T>
    requires Numeric<T>
T kubik(T x) {
    return x * x * x;
}

// Abbreviated function template (C++20)
auto kali(Numeric auto a, Numeric auto b) {
    return a * b;
}

// Concept untuk class template
template <Container C>
void cetakContainer(const C& c) {
    for (const auto& item : c) {
        cout << item << " ";
    }
    cout << endl;
}

// Requires expression yang kompleks
template <typename T>
concept Sortable = requires(T t) {
    requires totally_ordered<T>;          // Harus bisa dibandingkan
    { t.size() } -> convertible_to<size_t>; // Harus punya size()
    { t.begin() };                           // Harus iterable
    { t.end() };
};

// Concept dengan default
template <typename T>
    requires Numeric<T>
class Statistik {
private:
    vector<T> data;

public:
    void tambah(T val) { data.push_back(val); }

    double rataRata() const {
        if (data.empty()) return 0;
        T total = 0;
        for (const auto& d : data) total += d;
        return static_cast<double>(total) / data.size();
    }
};

int main() {
    // Concept-based function calls
    cout << "5^2 = " << kuadrat(5) << endl;
    cout << "3.14^2 = " << kuadrat(3.14) << endl;
    cout << "2^3 = " << kubik(2) << endl;
    cout << "3 * 4.5 = " << kali(3, 4.5) << endl;

    // kuadrat("hello");  // COMPILE ERROR: string bukan Numeric

    // Container concept
    vector<int> v = {1, 2, 3, 4, 5};
    cetakContainer(v);

    // Class with concept
    Statistik<int> stat;
    stat.tambah(80);
    stat.tambah(90);
    stat.tambah(85);
    cout << "Rata-rata: " << stat.rataRata() << endl;

    // Standard library concepts
    cout << "int is_integral: " << is_integral_v<int> << endl;
    cout << "double is_integral: " << is_integral_v<double> << endl;

    return 0;
}


10. Ranges (C++20)

Ranges memungkinkan pipeline operasi pada data dengan sintaks yang elegan dan lazy evaluation. Ini adalah evolusi dari STL algorithms.

C++20 — Ranges
#include <iostream>
#include <vector>
#include <ranges>
#include <string>
#include <algorithm>
using namespace std;

int main() {
    vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // Pipeline: filter genap, kali 3, ambil 3 terakhir
    auto result = data
        | views::filter([](int x) { return x % 2 == 0; })
        | views::transform([](int x) { return x * 3; })
        | views::take(3);

    cout << "Genap * 3 (ambil 3): ";
    for (int x : result) cout << x << " ";
    cout << endl;  // 6 12 18

    // Views adalah lazy — tidak menghitung sampai diiterasi
    auto even_squares = data
        | views::filter([](int x) { return x % 2 == 0; })
        | views::transform([](int x) { return x * x; });

    cout << "Kuadrat genap: ";
    for (int x : even_squares) cout << x << " ";
    cout << endl;  // 4 16 36 64 100

    // views::iota — generate sequence
    auto range1_10 = views::iota(1, 11);
    cout << "1-10: ";
    for (int x : range1_10) cout << x << " ";
    cout << endl;

    // views::take dan views::drop
    auto first3 = data | views::take(3);
    auto skip5 = data | views::drop(5);

    cout << "First 3: ";
    for (int x : first3) cout << x << " ";
    cout << endl;

    cout << "Skip 5: ";
    for (int x : skip5) cout << x << " ";
    cout << endl;

    // views::reverse
    auto reversed = data | views::reverse;
    cout << "Reversed: ";
    for (int x : reversed) cout << x << " ";
    cout << endl;

    // views::enumerate (C++23, tapi cek compiler)
    // for (auto [i, val] : data | views::enumerate) {
    //     cout << i << ": " << val << endl;
    // }

    // ranges algorithms
    vector<int> v = {5, 3, 8, 1, 9, 2, 7};
    ranges::sort(v);  // Lebih sederhana dari sort(v.begin(), v.end())
    cout << "Sorted: ";
    for (int x : v) cout << x << " ";
    cout << endl;

    // ranges::find
    auto it = ranges::find(v, 8);
    if (it != v.end()) {
        cout << "Found 8" << endl;
    }

    // Pipeline kompleks
    vector<string> nama = {"Budi", "Ani", "Dedi", "Sari", "Eka"};
    auto nama_panjang = nama
        | views::filter([](const string& s) { return s.length() > 3; })
        | views::transform([](string s) {
            transform(s.begin(), s.end(), s.begin(), ::toupper);
            return s;
        });

    cout << "Nama panjang (uppercase): ";
    for (const auto& n : nama_panjang) cout << n << " ";
    cout << endl;  // BUDI DEDI SARI

    return 0;
}


11. Coroutines (C++20)

Coroutines memungkinkan fungsi untuk suspend (jeda) dan resume (lanjut) eksekusinya. Ini berguna untuk async programming, generators, dan lazy evaluation.

C++20 — Coroutine Concepts
// Coroutines di C++20 membutuhkan <coroutine> header
// dan biasanya digunakan dengan library seperti cppcoro

#include <iostream>
#include <coroutine>
#include <optional>
using namespace std;

// Generator sederhana menggunakan coroutine
template <typename T>
struct Generator {
    struct promise_type {
        T value;

        Generator get_return_object() {
            return Generator{
                coroutine_handle<promise_type>::from_promise(*this)
            };
        }

        suspend_always initial_suspend() { return {}; }
        suspend_always final_suspend() noexcept { return {}; }

        suspend_always yield_value(T v) {
            value = v;
            return {};
        }

        void return_void() {}
        void unhandled_exception() { terminate(); }
    };

    coroutine_handle<promise_type> handle;

    ~Generator() {
        if (handle) handle.destroy();
    }

    optional<T> next() {
        if (!handle || handle.done()) return nullopt;
        handle.resume();
        return handle.promise().value;
    }
};

// Fibonacci generator sebagai coroutine
Generator<int> fibonacci() {
    int a = 0, b = 1;
    while (true) {
        co_yield a;
        int temp = a + b;
        a = b;
        b = temp;
    }
}

// Range generator
Generator<int> range(int start, int end, int step = 1) {
    for (int i = start; i < end; i += step) {
        co_yield i;
    }
}

int main() {
    // Fibonacci generator
    auto fib = fibonacci();
    cout << "Fibonacci pertama: ";
    for (int i = 0; i < 10; i++) {
        auto val = fib.next();
        if (val) cout << *val << " ";
    }
    cout << endl;

    // Range generator
    auto r = range(0, 20, 3);
    cout << "Range(0,20,3): ";
    while (auto val = r.next()) {
        cout << *val << " ";
    }
    cout << endl;

    return 0;
}

// Catatan: Coroutines adalah advanced topic.
// Untuk penggunaan sehari-hari, gunakan ranges dan algorithms
// sebagai alternatif yang lebih sederhana.
💡 Tips Belajar Modern C++

Mulai dari C++17 terlebih dahulu (didukung luas oleh semua compiler utama). Pelajari C++20 secara bertahap. Gunakan compiler flags seperti -std=c++20 dan selalu compile dengan warning flags -Wall -Wextra. Baca "Effective Modern C++" oleh Scott Meyers untuk best practices.



12. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Modern C++:

Pertanyaan 1: Apa keuntungan utama unique_ptr dibanding raw pointer?

a) Lebih cepat dari raw pointer
b) Otomatis dealokasi memori saat keluar scope, mencegah memory leak
c) Bisa di-share ke banyak pemilik
d) Menggunakan garbage collector

Pertanyaan 2: Apa yang dilakukan std::move()?

a) Memindahkan object ke lokasi memori lain
b) Meng-copy object secara deep copy
c) Meng-cast object menjadi rvalue reference, memungkinkan move constructor/assignment
d) Menghapus object dari memory

Pertanyaan 3: Apa fungsi dari constexpr?

a) Membuat variabel menjadi global
b) Mengharuskan komputasi dilakukan saat compile-time
c) Membuat fungsi menjadi inline
d) Menghapus variabel dari stack

Pertanyaan 4: Apa output dari kode berikut?
auto [a, b] = pair{10, 20}; cout << a + b;

a) 10 20
b) {10, 20}
c) 30
d) Error

Pertanyaan 5: Apa perbedaan antara std::optional dan pointer nullable (T*)?

a) optional memiliki value, pointer memiliki alamat memori
b) optional menyimpan object langsung di stack, lebih aman tanpa dangling pointer
c) Tidak ada perbedaan
d) optional hanya untuk tipe numeric
🔍 Zoom
100%
🎨 Tema