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++11 | 2011 | auto, lambda, move semantics, smart pointers, range-for |
| C++14 | 2014 | Generic lambda, return type deduction, variable template |
| C++17 | 2017 | Structured bindings, optional, variant, if constexpr, filesystem |
| C++20 | 2020 | Concepts, ranges, coroutines, modules, spaceship operator |
| C++23 | 2023 | std::expected, std::print, deducing this, flat_map |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β 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++
# 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_ptr | Eksklusif (satu pemilik) | Paling umum, default choice |
shared_ptr | Dibagi (reference counting) | Banyak pemilik, dealokasi saat ref count = 0 |
weak_ptr | Tidak memiliki (observer) | Mencegah circular reference |
#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;
}
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
#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.
#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.
#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.
#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.
#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.
#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.
#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.
#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.
// 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.
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++: