All Courses
Advanced Topics

Modern C++ Features (C++11/14/17/20/23)


Auto and Type Inference

auto

auto x = 42;              // int
auto y = 3.14;            // double
auto s = "hello"s;        // string (with s suffix)
auto v = vector<int>{1,2,3};

// With functions
auto add(int a, int b) -> int {
    return a + b;
}

// Return type deduction (C++14)
auto multiply(int a, int b) {
    return a * b;
}

decltype

int x = 10;
decltype(x) y = 20;       // int

// Get return type
decltype(add(1, 2)) result;

// Common with templates
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

Smart Pointers

unique_ptr

#include <memory>

// Exclusive ownership
unique_ptr<int> p1 = make_unique<int>(42);
cout << *p1 << endl;

// Transfer ownership
unique_ptr<int> p2 = move(p1);
// p1 is now nullptr

// Custom deleter
unique_ptr<FILE, decltype(&fclose)> file(fopen("f.txt", "r"), fclose);

// Array
unique_ptr<int[]> arr = make_unique<int[]>(10);

shared_ptr

// Shared ownership
shared_ptr<int> p1 = make_shared<int>(42);
shared_ptr<int> p2 = p1;  // Both own

cout << p1.use_count() << endl;  // 2

// Automatically deleted when last owner destroyed

weak_ptr

shared_ptr<int> shared = make_shared<int>(42);
weak_ptr<int> weak = shared;

// Check and access
if (auto locked = weak.lock()) {
    cout << *locked << endl;
}

// Check if still valid
if (!weak.expired()) {
    // Still valid
}

Move Semantics

Rvalue References

string s = "Hello";
string&& rref = move(s);  // s is now moved-from

// Move constructor
class MyClass {
    string data;
public:
    MyClass(MyClass&& other) noexcept
        : data(move(other.data)) {}

    MyClass& operator=(MyClass&& other) noexcept {
        data = move(other.data);
        return *this;
    }
};

std::move and std::forward

// move - cast to rvalue
vector<string> v;
string s = "Hello";
v.push_back(move(s));  // Moves instead of copies

// forward - perfect forwarding
template<typename T>
void wrapper(T&& arg) {
    process(forward<T>(arg));
}

Lambda Expressions

Basic Syntax

auto add = [](int a, int b) { return a + b; };
cout << add(1, 2) << endl;  // 3

// With captures
int x = 10;
auto addX = [x](int a) { return a + x; };
auto addXRef = [&x](int a) { return a + x; };

// Capture all
auto all = [=]() { };  // By value
auto allRef = [&]() { };  // By reference

Advanced Lambdas

// Mutable lambda
int count = 0;
auto counter = [count]() mutable { return ++count; };

// Generic lambda (C++14)
auto print = [](auto x) { cout << x << endl; };

// Init capture (C++14)
auto p = [ptr = make_unique<int>(42)]() { return *ptr; };

// Constexpr lambda (C++17)
auto square = [](int n) constexpr { return n * n; };

Structured Bindings

// C++17
// Pairs and tuples
pair<int, string> p = {1, "hello"};
auto [id, name] = p;

// Arrays
int arr[] = {1, 2, 3};
auto [a, b, c] = arr;

// Structs
struct Point { int x, y; };
Point pt = {10, 20};
auto [x, y] = pt;

// Maps
map<string, int> m = {{"a", 1}, {"b", 2}};
for (const auto& [key, value] : m) {
    cout << key << ": " << value << endl;
}

Optional, Variant, Any

optional

#include <optional>

optional<int> findValue(bool found) {
    if (found) return 42;
    return nullopt;
}

auto result = findValue(true);
if (result) {
    cout << *result << endl;
}

// value_or
cout << result.value_or(-1) << endl;

variant

#include <variant>

variant<int, string, double> v;
v = 42;
v = "hello";
v = 3.14;

// Access
cout << get<double>(v) << endl;

// Visit
visit([](auto&& arg) { cout << arg << endl; }, v);

any

#include <any>

any a = 42;
a = string("hello");
a = 3.14;

if (a.type() == typeid(double)) {
    cout << any_cast<double>(a) << endl;
}

String View

#include <string_view>

// Non-owning view of string
string_view sv = "Hello, World!";

// Efficient substring
string_view sub = sv.substr(0, 5);  // "Hello"

// From string
string s = "Test";
string_view sv2 = s;

// Function parameter
void print(string_view sv) {
    cout << sv << endl;
}

print("literal");     // No allocation
print(s);             // No copy

Constexpr and Compile-Time

constexpr

// Compile-time constant
constexpr int square(int n) {
    return n * n;
}

constexpr int val = square(5);  // Computed at compile time

// constexpr if (C++17)
template<typename T>
auto process(T value) {
    if constexpr (is_integral_v<T>) {
        return value * 2;
    } else {
        return value;
    }
}

consteval (C++20)

consteval int mustBeCompileTime(int n) {
    return n * n;
}

constexpr int x = mustBeCompileTime(5);  // OK
// int y = mustBeCompileTime(runtime);   // ERROR

Ranges (C++20)

#include <ranges>

vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// Pipe syntax
auto result = v
    | views::filter([](int n) { return n % 2 == 0; })
    | views::transform([](int n) { return n * n; });

for (int x : result) {
    cout << x << " ";  // 4 16 36 64 100
}

// views
views::iota(1, 10)           // 1-9
views::take(v, 3)            // First 3
views::drop(v, 2)            // Skip 2
views::reverse(v)            // Reversed
views::keys(map)             // Map keys
views::values(map)           // Map values

Ranges Visual Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    RANGES PIPELINE VISUALIZATION                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                          β”‚
β”‚   Input: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}                                β”‚
β”‚            β”‚                                                             β”‚
β”‚            β–Ό                                                             β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                β”‚
β”‚   β”‚ views::filter      β”‚  Keep only even numbers                        β”‚
β”‚   β”‚ (n % 2 == 0)       β”‚                                                β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                β”‚
β”‚            β”‚                                                             β”‚
β”‚            β–Ό                                                             β”‚
β”‚   {2, 4, 6, 8, 10}                                                      β”‚
β”‚            β”‚                                                             β”‚
β”‚            β–Ό                                                             β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                β”‚
β”‚   β”‚ views::transform   β”‚  Square each number                            β”‚
β”‚   β”‚ (n * n)            β”‚                                                β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                β”‚
β”‚            β”‚                                                             β”‚
β”‚            β–Ό                                                             β”‚
β”‚   Output: {4, 16, 36, 64, 100}                                          β”‚
β”‚                                                                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   KEY: Views are lazy - no intermediate containers created!             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Common Range Adaptors

// take - first N elements
views::take(v, 3)                    // {1, 2, 3}

// drop - skip first N elements
views::drop(v, 3)                    // {4, 5, 6, 7, 8, 9, 10}

// take_while / drop_while
views::take_while(v, [](int n) { return n < 5; })  // {1, 2, 3, 4}

// filter - keep elements matching predicate
views::filter(v, [](int n) { return n > 5; })      // {6, 7, 8, 9, 10}

// transform - apply function to each element
views::transform(v, [](int n) { return n * 2; })   // {2, 4, 6, ...}

// reverse - reverse order
views::reverse(v)                    // {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}

// split - split by delimiter
views::split(str, ' ')               // Split string by spaces

// join - flatten nested ranges
views::join(nested_vec)              // Flatten 2D to 1D

// zip (C++23) - combine multiple ranges
views::zip(v1, v2)                   // Pairs of elements

// enumerate (C++23) - add index
views::enumerate(v)                  // (0, elem0), (1, elem1), ...

Range Algorithms

#include <ranges>
#include <algorithm>

vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};

// Range-based algorithms (take range directly)
ranges::sort(v);
ranges::find(v, 5);
ranges::count(v, 1);
ranges::copy(v, output.begin());

// With projections
struct Person { string name; int age; };
vector<Person> people;

// Sort by age using projection
ranges::sort(people, {}, &Person::age);

// Find by name
auto it = ranges::find(people, "Alice", &Person::name);

Concepts (C++20)

Concepts provide a way to constrain template parameters with readable, named requirements.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          CONCEPTS OVERVIEW                               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                          β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚   β”‚   Template   β”‚ ───► β”‚   Concept    β”‚ ───► β”‚   Better     β”‚         β”‚
β”‚   β”‚   Error Msg  β”‚      β”‚   Constraint β”‚      β”‚   Diagnosticsβ”‚         β”‚
β”‚   β”‚   (Cryptic)  β”‚      β”‚   (Named)    β”‚      β”‚   (Clear)    β”‚         β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚                                                                          β”‚
β”‚   Before C++20:                                                          β”‚
β”‚   error: no match for 'operator<<' ...                                  β”‚
β”‚   ... 50 lines of template instantiation trace ...                      β”‚
β”‚                                                                          β”‚
β”‚   With Concepts:                                                         β”‚
β”‚   error: constraint 'Printable<T>' not satisfied                        β”‚
β”‚                                                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Defining Concepts

#include <concepts>

// Basic concept definition
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;

// Concept with requirements
template<typename T>
concept Printable = requires(T t) {
    { std::cout << t } -> std::same_as<std::ostream&>;
};

// Concept with multiple requirements
template<typename T>
concept Container = requires(T c) {
    { c.begin() } -> std::input_or_output_iterator;
    { c.end() } -> std::input_or_output_iterator;
    { c.size() } -> std::convertible_to<std::size_t>;
    typename T::value_type;
};

// Compound requirements
template<typename T>
concept Hashable = requires(T a) {
    { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};

Using Concepts

// Method 1: requires clause
template<typename T>
requires Numeric<T>
T add(T a, T b) { return a + b; }

// Method 2: Concept as type constraint
template<Numeric T>
T multiply(T a, T b) { return a * b; }

// Method 3: Trailing requires
template<typename T>
T divide(T a, T b) requires Numeric<T> { return a / b; }

// Method 4: Abbreviated function template
void print(Printable auto value) {
    std::cout << value << std::endl;
}

Standard Library Concepts

#include <concepts>

// Type concepts
std::same_as<T, U>           // T is same as U
std::derived_from<D, B>      // D derives from B
std::convertible_to<From, To>// From can convert to To

// Comparison concepts
std::equality_comparable<T>  // == and !=
std::totally_ordered<T>      // <, <=, >, >=, ==

// Object concepts
std::movable<T>              // Move constructible/assignable
std::copyable<T>             // Copy + movable
std::regular<T>              // Copyable + default init + equality

// Arithmetic concepts
std::integral<T>             // int, long, char, etc.
std::floating_point<T>       // float, double
std::signed_integral<T>      // Signed integers
std::unsigned_integral<T>    // Unsigned integers

// Iterator concepts
std::input_iterator<T>
std::forward_iterator<T>
std::bidirectional_iterator<T>
std::random_access_iterator<T>
std::contiguous_iterator<T>

// Range concepts
std::ranges::range<T>
std::ranges::sized_range<T>
std::ranges::input_range<T>

Concept Example: Type-Safe Container

template<typename T>
concept Arithmetic = std::integral<T> || std::floating_point<T>;

template<Arithmetic T>
class NumericVector {
    std::vector<T> data_;
public:
    T sum() const {
        return std::accumulate(data_.begin(), data_.end(), T{});
    }

    T average() const requires std::floating_point<T> {
        return sum() / data_.size();
    }

    void push(T value) { data_.push_back(value); }
};

// NumericVector<int> vi;      // OK
// NumericVector<double> vd;   // OK
// NumericVector<string> vs;   // ERROR: string is not Arithmetic

Coroutines (C++20)

Coroutines are functions that can suspend execution and resume later.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      COROUTINE EXECUTION FLOW                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                          β”‚
β”‚   Caller                        Coroutine                               β”‚
β”‚     β”‚                             β”‚                                      β”‚
β”‚     β”‚  create()                   β”‚                                      β”‚
β”‚     │────────────────────────────►│                                      β”‚
β”‚     β”‚                             β”‚ Execute until suspend               β”‚
β”‚     │◄────────────────────────────│                                      β”‚
β”‚     β”‚  (suspended)                β”‚                                      β”‚
β”‚     β”‚                             β”‚                                      β”‚
β”‚     β”‚  resume()                   β”‚                                      β”‚
β”‚     │────────────────────────────►│                                      β”‚
β”‚     β”‚                             β”‚ Continue execution                  β”‚
β”‚     β”‚                             β”‚ until next suspend                  β”‚
β”‚     │◄────────────────────────────│                                      β”‚
β”‚     β”‚  (value or suspended)       β”‚                                      β”‚
β”‚     β”‚                             β”‚                                      β”‚
β”‚     β”‚  resume()                   β”‚                                      β”‚
β”‚     │────────────────────────────►│                                      β”‚
β”‚     β”‚                             β”‚ Final execution                     β”‚
β”‚     │◄────────────────────────────│                                      β”‚
β”‚     β”‚  (done)                     β”‚                                      β”‚
β”‚     β–Ό                             β–Ό                                      β”‚
β”‚                                                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Generator Example

#include <coroutine>
#include <iostream>

// Simple generator that yields values
template<typename T>
struct Generator {
    struct promise_type {
        T current_value;

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

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

        std::suspend_always yield_value(T value) {
            current_value = value;
            return {};
        }

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

    std::coroutine_handle<promise_type> handle;

    Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
    ~Generator() { if (handle) handle.destroy(); }

    // Iterator interface
    struct iterator {
        std::coroutine_handle<promise_type> handle;

        iterator& operator++() {
            handle.resume();
            return *this;
        }

        T operator*() const { return handle.promise().current_value; }
        bool operator!=(std::default_sentinel_t) const { return !handle.done(); }
    };

    iterator begin() {
        handle.resume();
        return {handle};
    }

    std::default_sentinel_t end() { return {}; }
};

// Usage
Generator<int> range(int start, int end) {
    for (int i = start; i < end; ++i) {
        co_yield i;  // Suspend and yield value
    }
}

int main() {
    for (int x : range(0, 5)) {
        std::cout << x << " ";  // 0 1 2 3 4
    }
}

Coroutine Keywords

co_yield expr;    // Suspend and yield a value
co_return expr;   // Complete with a value
co_await expr;    // Suspend until operation completes

Modules (C++20)

Modules replace the traditional header/source separation with a more efficient compilation model.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  HEADERS vs MODULES COMPARISON                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚         HEADERS                β”‚            MODULES                      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                β”‚                                         β”‚
β”‚  // header.h                   β”‚  // math.ixx (module interface)        β”‚
β”‚  #pragma once                  β”‚  export module math;                    β”‚
β”‚  #include <vector>             β”‚                                         β”‚
β”‚  class MyClass { ... };        β”‚  export int add(int a, int b) {        β”‚
β”‚                                β”‚      return a + b;                      β”‚
β”‚  // Parsed every #include      β”‚  }                                      β”‚
β”‚  // Macros leak across files   β”‚                                         β”‚
β”‚  // Slow compilation           β”‚  // Compiled once, imported quickly    β”‚
β”‚                                β”‚  // No macro leakage                    β”‚
β”‚  // source.cpp                 β”‚  // Much faster compilation             β”‚
β”‚  #include "header.h"           β”‚                                         β”‚
β”‚  // ... uses MyClass           β”‚  // main.cpp                            β”‚
β”‚                                β”‚  import math;                           β”‚
β”‚                                β”‚  int x = add(1, 2);                     β”‚
β”‚                                β”‚                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Module Interface Unit

// math.ixx (or math.cppm)
export module math;

// Import standard library (C++23 style)
import <iostream>;

// Private (not exported)
int internal_helper(int x) {
    return x * 2;
}

// Public (exported)
export int add(int a, int b) {
    return a + b;
}

export int multiply(int a, int b) {
    return a * b;
}

export class Calculator {
public:
    int compute(int a, int b) {
        return internal_helper(a) + b;
    }
};

Module Implementation Unit

// math_impl.cpp
module math;  // No 'export' - this is implementation

// Can access non-exported symbols
int optimized_compute(int a, int b) {
    return internal_helper(a + b);
}

Using Modules

// main.cpp
import math;
import <iostream>;

int main() {
    std::cout << add(1, 2) << std::endl;       // 3
    std::cout << multiply(3, 4) << std::endl;  // 12

    Calculator calc;
    std::cout << calc.compute(5, 6) << std::endl;
}


std::format (C++20)

Type-safe, extensible formatting library replacing printf and stringstream.

#include <format>
#include <iostream>

// Basic formatting
std::string s1 = std::format("Hello, {}!", "World");
std::string s2 = std::format("{} + {} = {}", 1, 2, 3);

// Positional arguments
std::string s3 = std::format("{1} before {0}", "second", "first");
// "first before second"

// Format specifiers
std::format("{:d}", 42);      // "42" (decimal)
std::format("{:x}", 255);     // "ff" (hex)
std::format("{:X}", 255);     // "FF" (uppercase hex)
std::format("{:b}", 5);       // "101" (binary)
std::format("{:o}", 8);       // "10" (octal)

// Width and alignment
std::format("{:10}", "hi");   // "hi        " (left, width 10)
std::format("{:>10}", "hi");  // "        hi" (right align)
std::format("{:^10}", "hi");  // "    hi    " (center)
std::format("{:*^10}", "hi"); // "****hi****" (fill with *)

// Floating point
std::format("{:.2f}", 3.14159);  // "3.14" (2 decimals)
std::format("{:e}", 1234.5);     // "1.234500e+03" (scientific)
std::format("{:g}", 1234.5);     // "1234.5" (general)

Format Specifier Syntax

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     FORMAT SPECIFIER SYNTAX                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                          β”‚
β”‚   {:[[fill]align][sign][#][0][width][.precision][type]}                 β”‚
β”‚                                                                          β”‚
β”‚   Examples:                                                              β”‚
β”‚   {:>10}      Right align, width 10                                     β”‚
β”‚   {:0>10}     Right align, pad with zeros                               β”‚
β”‚   {:.3f}      3 decimal places, fixed-point                             β”‚
β”‚   {:+.2f}     Always show sign, 2 decimals                              β”‚
β”‚   {:#x}       Hex with 0x prefix                                        β”‚
β”‚   {:*^20}     Center, width 20, fill with *                             β”‚
β”‚                                                                          β”‚
β”‚   align: < (left) > (right) ^ (center)                                  β”‚
β”‚   sign:  + (always) - (negative only) space (space for positive)        β”‚
β”‚   type:  d b o x X (integers) e E f F g G (floats) s (string)          β”‚
β”‚                                                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Custom Formatter

struct Point {
    int x, y;
};

template<>
struct std::formatter<Point> {
    constexpr auto parse(std::format_parse_context& ctx) {
        return ctx.begin();
    }

    auto format(const Point& p, std::format_context& ctx) const {
        return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
    }
};

// Usage
Point p{10, 20};
std::cout << std::format("Point: {}", p);  // "Point: (10, 20)"

std::print (C++23)

#include <print>

std::print("Hello, {}!\n", "World");
std::println("Line with newline: {}", 42);

std::span (C++20)

A non-owning view over contiguous memory (like string_view for arrays).

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          std::span CONCEPT                               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                          β”‚
β”‚   Original Array:  [10][20][30][40][50]                                 β”‚
β”‚                     β–²               β–²                                    β”‚
β”‚                     β”‚               β”‚                                    β”‚
β”‚                   data()         data()+size()                          β”‚
β”‚                                                                          β”‚
β”‚   span<int>:  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                              β”‚
β”‚               β”‚ data_ ───┼──► [10][20][30][40][50]                      β”‚
β”‚               β”‚ size_: 5 β”‚                                              β”‚
β”‚               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                              β”‚
β”‚                                                                          β”‚
β”‚   subspan:    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                              β”‚
β”‚               β”‚ data_ ───┼──────► [20][30][40]                          β”‚
β”‚               β”‚ size_: 3 β”‚                                              β”‚
β”‚               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                              β”‚
β”‚                                                                          β”‚
β”‚   βœ“ No copying        βœ“ Safe bounds checking                           β”‚
β”‚   βœ“ Works with arrays, vectors, C arrays                               β”‚
β”‚                                                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
#include <span>
#include <vector>
#include <array>

// Function that works with any contiguous container
void process(std::span<int> data) {
    for (int& x : data) {
        x *= 2;
    }
}

int main() {
    // Works with C arrays
    int arr[] = {1, 2, 3, 4, 5};
    process(arr);

    // Works with vectors
    std::vector<int> vec = {1, 2, 3, 4, 5};
    process(vec);

    // Works with std::array
    std::array<int, 5> sarr = {1, 2, 3, 4, 5};
    process(sarr);

    // Create span explicitly
    std::span<int> sp(arr);
    std::span<int> sp2(vec.data(), vec.size());
}

Span Operations

std::span<int> s(vec);

s.size();           // Number of elements
s.size_bytes();     // Size in bytes
s.empty();          // Check if empty
s.data();           // Pointer to first element

s.front();          // First element
s.back();           // Last element
s[2];               // Element access

s.first<3>();       // First 3 elements (static extent)
s.first(3);         // First 3 elements (dynamic extent)
s.last<2>();        // Last 2 elements
s.last(2);

s.subspan<1, 3>();  // Elements 1-3 (static)
s.subspan(1, 3);    // Elements 1-3 (dynamic)

Fixed-Size Span

// Static extent - size known at compile time
std::span<int, 5> fixed_span(arr);

// Dynamic extent - size determined at runtime
std::span<int> dynamic_span(vec);

// Function with fixed size requirement
void process_three(std::span<int, 3> data) {
    // Guaranteed 3 elements
}

int arr3[3] = {1, 2, 3};
process_three(arr3);  // OK
// process_three(arr);  // ERROR: arr has 5 elements

std::expected (C++23)

A type for returning either a value or an error (better than exceptions for expected errors).

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       std::expected CONCEPT                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                          β”‚
β”‚   expected<T, E>  - Contains either T (value) or E (error)              β”‚
β”‚                                                                          β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                       β”‚
β”‚   β”‚   has_value()   β”‚         β”‚   !has_value()  β”‚                       β”‚
β”‚   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€         β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                       β”‚
β”‚   β”‚    Success      β”‚         β”‚     Error       β”‚                       β”‚
β”‚   β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚         β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚                       β”‚
β”‚   β”‚   β”‚ value T β”‚   β”‚         β”‚   β”‚ error E β”‚   β”‚                       β”‚
β”‚   β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚         β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚                       β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β”‚
β”‚                                                                          β”‚
β”‚   Comparison with alternatives:                                          β”‚
β”‚                                                                          β”‚
β”‚   optional<T>     : Value or nothing (no error info)                    β”‚
β”‚   variant<T, E>   : Type-safe union (less ergonomic)                    β”‚
β”‚   Exceptions      : For exceptional cases, not expected errors          β”‚
β”‚   expected<T, E>  : Value OR error with full error details βœ“           β”‚
β”‚                                                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
#include <expected>
#include <string>

enum class ParseError {
    InvalidFormat,
    OutOfRange,
    Empty
};

std::expected<int, ParseError> parse_int(const std::string& s) {
    if (s.empty()) {
        return std::unexpected(ParseError::Empty);
    }

    try {
        int value = std::stoi(s);
        return value;  // Implicit conversion to expected
    } catch (const std::invalid_argument&) {
        return std::unexpected(ParseError::InvalidFormat);
    } catch (const std::out_of_range&) {
        return std::unexpected(ParseError::OutOfRange);
    }
}

// Usage
auto result = parse_int("42");

if (result) {  // or result.has_value()
    std::cout << "Value: " << *result << std::endl;
} else {
    switch (result.error()) {
        case ParseError::InvalidFormat:
            std::cout << "Invalid format" << std::endl;
            break;
        case ParseError::OutOfRange:
            std::cout << "Out of range" << std::endl;
            break;
        case ParseError::Empty:
            std::cout << "Empty string" << std::endl;
            break;
    }
}

Expected Operations

std::expected<int, std::string> exp = 42;

exp.has_value();        // true
exp.value();            // 42 (throws if error)
*exp;                   // 42 (undefined if error)
exp.value_or(0);        // 42 (or 0 if error)

exp.error();            // Error value (undefined if has value)

// Monadic operations (C++23)
exp.and_then([](int x) -> std::expected<int, std::string> {
    return x * 2;
});

exp.transform([](int x) { return x * 2; });

exp.or_else([](const std::string& err) -> std::expected<int, std::string> {
    std::cout << "Error: " << err << std::endl;
    return 0;  // Default value
});

Chaining Expected Operations

std::expected<std::string, Error> read_file(const std::string& path);
std::expected<Config, Error> parse_config(const std::string& content);
std::expected<App, Error> create_app(const Config& config);

// Chain operations (C++23)
auto result = read_file("config.json")
    .and_then(parse_config)
    .and_then(create_app);

if (result) {
    result->run();
} else {
    handle_error(result.error());
}

Three-Way Comparison (C++20)

The spaceship operator <=> provides all comparison operators in one.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   THREE-WAY COMPARISON (SPACESHIP)                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                          β”‚
β”‚   a <=> b  returns one of:                                              β”‚
β”‚                                                                          β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚   β”‚ strong_ordering β”‚ weak_ordering   β”‚ partial_orderingβ”‚              β”‚
β”‚   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€              β”‚
β”‚   β”‚ β€’ less          β”‚ β€’ less          β”‚ β€’ less          β”‚              β”‚
β”‚   β”‚ β€’ equal         β”‚ β€’ equivalent    β”‚ β€’ equivalent    β”‚              β”‚
β”‚   β”‚ β€’ greater       β”‚ β€’ greater       β”‚ β€’ greater       β”‚              β”‚
β”‚   β”‚                 β”‚                 β”‚ β€’ unordered     β”‚              β”‚
β”‚   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€              β”‚
β”‚   β”‚ int, char,      β”‚ Case-insensitiveβ”‚ floating-point  β”‚              β”‚
β”‚   β”‚ pointers        β”‚ strings         β”‚ (NaN issues)    β”‚              β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β”‚                                                                          β”‚
β”‚   One <=> definition generates: ==, !=, <, <=, >, >=                    β”‚
β”‚                                                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
#include <compare>

struct Point {
    int x, y;

    // Default spaceship operator
    auto operator<=>(const Point&) const = default;
};

Point p1{1, 2}, p2{1, 3};
bool less = (p1 < p2);     // true (compares x, then y)
bool equal = (p1 == p2);   // false

// Custom spaceship
struct Version {
    int major, minor, patch;

    std::strong_ordering operator<=>(const Version& other) const {
        if (auto cmp = major <=> other.major; cmp != 0) return cmp;
        if (auto cmp = minor <=> other.minor; cmp != 0) return cmp;
        return patch <=> other.patch;
    }

    bool operator==(const Version&) const = default;
};

Version v1{1, 2, 3}, v2{1, 3, 0};
bool newer = (v2 > v1);  // true

Additional C++23 Features

if consteval

constexpr int compute(int x) {
    if consteval {
        // Compile-time only code
        return x * 2;  // Can use consteval functions
    } else {
        // Runtime code
        return x * 2;  // Can use non-constexpr operations
    }
}

Deducing this (Explicit Object Parameter)

struct Widget {
    // Single implementation for both const and non-const
    template<typename Self>
    auto& value(this Self&& self) {
        return self.value_;
    }

    // CRTP without inheritance
    void print(this auto&& self) {
        std::cout << self.name() << std::endl;
    }

private:
    int value_;
};

std::mdspan (Multidimensional Span)

#include <mdspan>

std::vector<int> data(12);
std::iota(data.begin(), data.end(), 0);

// View as 3x4 matrix
std::mdspan matrix(data.data(), 3, 4);

for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 4; ++j) {
        std::cout << matrix[i, j] << " ";
    }
    std::cout << "\n";
}
// 0 1 2 3
// 4 5 6 7
// 8 9 10 11

std::stacktrace

#include <stacktrace>

void problematic_function() {
    auto trace = std::stacktrace::current();
    std::cout << std::to_string(trace);
}

Lambda Improvements

// Attributes on lambdas
auto f = []() [[nodiscard]] { return 42; };

// static operator() (no captures, more efficient)
auto add = [](int a, int b) static { return a + b; };

Cheat Sheet

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    MODERN C++ QUICK REFERENCE                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                          β”‚
β”‚  C++11/14:                              C++17:                          β”‚
β”‚  ─────────                              ──────                          β”‚
β”‚  auto, decltype                         structured bindings             β”‚
β”‚  unique_ptr, shared_ptr                 optional, variant, any          β”‚
β”‚  move semantics                         string_view                     β”‚
β”‚  lambdas                                if constexpr                    β”‚
β”‚  range-based for                        fold expressions                β”‚
β”‚                                                                          β”‚
β”‚  C++20:                                 C++23:                          β”‚
β”‚  ──────                                 ──────                          β”‚
β”‚  concepts                               std::expected                   β”‚
β”‚  ranges                                 std::print/println              β”‚
β”‚  coroutines                             deducing this                   β”‚
β”‚  modules                                std::mdspan                     β”‚
β”‚  std::format                            std::stacktrace                 β”‚
β”‚  std::span                              if consteval                    β”‚
β”‚  three-way comparison (<=>)             ranges::zip, enumerate          β”‚
β”‚  consteval, constinit                   std::generator                  β”‚
β”‚                                                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
// Type inference
auto x = expr;
decltype(expr) y;

// Smart pointers
unique_ptr<T> p = make_unique<T>(args);
shared_ptr<T> p = make_shared<T>(args);
weak_ptr<T> w = sharedPtr;

// Move semantics
T&& rref;
move(x);
forward<T>(x);

// Lambdas
[captures](params) { body }
[=] [&] [x] [&x] [x = expr]

// Structured bindings
auto [a, b] = pair;
auto& [x, y] = struct;

// Optional/Variant/Any
optional<T>, nullopt, .value_or()
variant<T1,T2>, get<T>(), visit()
any, any_cast<T>()

// string_view & span
string_view sv = str;
span<T> s = container;

// constexpr
constexpr T func();
if constexpr (cond) { }
consteval T must_be_compile_time();

// Concepts
template<Concept T> or requires Concept<T>

// Ranges
v | views::filter(pred) | views::transform(fn)

// Format
std::format("{} {}", arg1, arg2);
std::print("{}\n", value);  // C++23

// Expected (C++23)
std::expected<T, E> result = func();
result.value_or(default);
result.and_then(next_func);

// Three-way comparison
auto cmp = a <=> b;  // strong_ordering, weak_ordering, or partial_ordering

Compile & Run

# C++17
g++ -std=c++17 -Wall examples.cpp -o examples && ./examples

# C++20
g++ -std=c++20 -Wall examples.cpp -o examples && ./examples

# C++23 (requires GCC 13+ or Clang 17+)
g++ -std=c++23 -Wall examples.cpp -o examples && ./examples

# With modules (C++20)
g++ -std=c++20 -fmodules-ts module.cpp main.cpp -o main