FFlauron
C++ Pro
Безкоштовний урок-прев'ю

Сучасний C++ (17/20/23): auto, structured bindings, optional, format

C++ суттєво змінився між 2011 і 2023. Сучасний C++20/23 — це майже інша мова порівняно з C++03. Цей курс — про те, як C++ пишуть зараз, у production-проєктах 2026.

Що приніс сучасний C++

Стандарт Рік Ключові фічі
C++11 2011 auto, lambdas, range-for, smart pointers, move-семантика, nullptr, constexpr
C++14 2014 generic lambdas, auto return, make_unique, binary literals
C++17 2017 structured bindings, std::optional, std::variant, std::filesystem, if constexpr
C++20 2020 concepts, ranges, coroutines, modules, std::format, std::span
C++23 2023 std::print, std::expected, std::mdspan, deducing this

У 2026 актуальний компілятор підтримує C++23. Пишемо C++20/23, не C++03/11.

auto — type deduction

auto i = 42;                    // int
auto d = 3.14;                  // double
auto s = "hello";                // const char*
auto v = std::vector<int>{1,2,3}; // std::vector<int>
auto x = std::string("text");    // std::string

const auto& ref = some_vector;   // const reference — без копії

Використовуй auto усюди, де тип очевидний з ініціалізатора або занадто довгий (std::map<std::string, std::vector<std::string>>::iteratorauto).

Structured bindings (C++17)

Розпакувати tuple/pair/struct:

#include <map>
std::map<std::string, int> ages = {{"Olena", 25}, {"Ivan", 30}};

for (const auto& [name, age] : ages) {
    std::cout << name << ": " << age << "\n";
}

// Своя структура
struct Point { int x, y; };
Point p{3, 4};
auto [x, y] = p;

Аналог Python for name, age in items.items().

range-based for + init-statement (C++20)

// Простий
for (auto x : container) { ... }

// З init-statement (C++20):
for (auto v = compute(); auto& x : v) {
    // ...
}

Можна оголосити змінну в самому циклі — менший scope.

if/switch з init-statement (C++17)

if (auto it = map.find(key); it != map.end()) {
    // використовуємо it лише тут
    use(it->second);
}
// it тут уже нема

switch (auto err = compute(); err.code) {
    case 0: ...
    case 1: ...
}

Lambda — modern

auto square = [](int n) { return n * n; };

// З захопленням
int multiplier = 5;
auto multiply = [multiplier](int n) { return n * multiplier; };

// За посиланням
auto counter = [&multiplier]() { ++multiplier; };

// Generic lambda (C++14+)
auto add = [](auto a, auto b) { return a + b; };
add(2, 3);          // int
add(2.5, 3.5);      // double

// Template lambda (C++20)
auto print_all = []<typename T>(const std::vector<T>& v) {
    for (const auto& x : v) std::cout << x << " ";
};

Initializer lists

std::vector<int> v = {1, 2, 3, 4};
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
auto pair = std::pair{1, "text"};   // CTAD (C++17 deduction)

std::optional (C++17)

Замість T* для «може бути значення»:

#include <optional>

std::optional<User> findUser(int id) {
    if (id < 0) return std::nullopt;
    return User{id, "Olena"};
}

auto user = findUser(42);
if (user) {
    std::cout << user->name;
}
// Або через value_or:
std::string name = user.has_value() ? user->name : "guest";

Безпечніше за nullptr/-1 для індикації «нема значення».

std::variant — type-safe union

#include <variant>

std::variant<int, std::string, double> v;
v = 42;
v = "text";
v = 3.14;

// Перевірити тип
if (std::holds_alternative<int>(v)) {
    int n = std::get<int>(v);
}

// std::visit — pattern matching
std::visit([](const auto& x) {
    std::cout << x << "\n";
}, v);

Замінює union-и з C, але типобезпечно.

std::format / std::print (C++20/23)

Сучасне форматування рядків. Замість printf чи cout <<:

#include <format>
#include <print>      // C++23

std::string s = std::format("Name: {}, Age: {}", name, age);
std::print("Sum: {}, Product: {}\n", a + b, a * b);

Python-like синтаксис, типобезпечно (на відміну від printf).

std::print потребує GCC 14+/Clang 18+. Якщо нема — використовуй fmt::print з бібліотеки {fmt} (саме її скопіювали в стандарт).

std::span (C++20) — view на масив

#include <span>

void print(std::span<int> data) {
    for (int x : data) std::cout << x << " ";
}

std::vector<int> v = {1, 2, 3, 4};
int arr[] = {5, 6, 7};

print(v);            // ✓
print(arr);          // ✓
print({v.data(), 2}); // ✓ перші 2 елементи

span — не власник даних, просто «вид». Швидкий, без копіювання, на будь-яку колекцію.

constexpr — обчислення в compile-time

constexpr int factorial(int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
}

constexpr int x = factorial(10);   // 3628800, обчислено компілятором

constexpr if (C++17):

template<typename T>
auto get_value(T t) {
    if constexpr (std::is_pointer_v<T>) {
        return *t;
    } else {
        return t;
    }
}

if constexpr вибирає гілку в compile-time — не-обрана навіть не компілюється.

Інструменти

  • CMake — стандарт збірки (не g++ file.cpp -o out руками)
  • Compiler Explorer (godbolt.org) — як твій код компілюється
  • clang-format — автоформатування
  • clang-tidy — лінтер
  • ASAN/UBSAN/TSAN — sanitizers (детальніше в m11)
  • vcpkg / Conan — менеджери пакетів

Тест: перевір себе

  1. У чому суть if constexpr?
  2. Що дає structured bindings?
  3. Як використати std::optional для функції, яка «може повернути значення»?
  4. Що таке std::span і чим відрізняється від std::vector?
  5. Чому std::format краще за printf?
Відповіді
  1. Вибір гілки коду в compile-time. Не-обрана гілка навіть не компілюється — це дозволяє використовувати фічі, що не існують для деяких типів. Замінює SFINAE-трюки.
  2. Розпакування tuple/pair/struct у окремі змінні. Замість auto pair = func(); auto first = pair.first; auto second = pair.second;auto [first, second] = func();. Особливо зручно в for по map.
  3. std::optional<User> find(int id) {
        if (notFound) return std::nullopt;
        return User{...};
    }
    if (auto u = find(1)) { use(u->name); }
    
  4. span — лише вид (view) на існуючу пам'ять, не власник. Дешевий (без копіювання), не керує lifecycle. vector — власник, керує пам'яттю, можна push_back.
  5. Типобезпечно (на відміну від printf, де %d для float = UB). Простий синтаксис {} як Python f-strings. Розширюваний — можна додавати власні форматтери для своїх типів.

Сподобався урок?

Придбайте повний курс, щоб отримати доступ до всіх уроків.

Перейти до курсу