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

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++
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                TIMELINE MODERN C++                          β”‚
β”‚                                                             β”‚
β”‚  2011        2014        2017        2020        2023      β”‚
β”‚   β”‚           β”‚           β”‚           β”‚           β”‚        β”‚
β”‚   β–Ό           β–Ό           β–Ό           β–Ό           β–Ό        β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚ β”‚C++11β”‚   β”‚C++14β”‚   β”‚  C++17  β”‚ β”‚  C++20  β”‚ β”‚  C++23  β”‚  β”‚
β”‚ β”‚auto β”‚   β”‚Genericβ”‚ β”‚optional β”‚ β”‚concepts β”‚ β”‚expected β”‚  β”‚
β”‚ β”‚lambdaβ”‚  β”‚lambda β”‚ β”‚variant  β”‚ β”‚ranges   β”‚ β”‚print    β”‚  β”‚
β”‚ β”‚move β”‚   β”‚return β”‚ β”‚struct.  β”‚ β”‚coroutinesβ”‚ β”‚deducing β”‚  β”‚
β”‚ β”‚smartβ”‚   β”‚type   β”‚ β”‚bindings β”‚ β”‚modules  β”‚ β”‚this     β”‚  β”‚
β”‚ β”‚ptr  β”‚   β”‚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