C++ Handbook

C++ is a general-purpose, multi-paradigm language created by Bjarne Stroustrup as a superset of C. It supports procedural, object-oriented, generic, and functional styles simultaneously. Every C program is valid C++, but C++ adds classes, templates, RAII, the STL, exceptions, and — since C++11 — move semantics, lambdas, smart pointers, and constexpr. It compiles to native machine code with no GC. Unreal Engine, Chrome, Firefox, LLVM, and most AAA games are written in C++.

Pick C++ when

  • You need C-level performance with high-level abstractions — zero-cost abstractions (templates, inline, constexpr) mean you pay only for what you use. Game engines, physics simulators, and trading systems live here.
  • You are building a real-time system with predictable latency — no GC pauses, manual memory control, and RAII give you deterministic resource management.
  • You need to interoperate with a C codebase and add OOP on top — C++ is a strict superset; you can gradually modernize a C project file by file.
  • Template metaprogramming or generic libraries are the goal — the STL, Eigen, Boost, and CUDA all rely on C++ templates for compile-time generics with zero runtime cost.
  • Embedded targets with some resources — you get C-level control but can use classes and RAII. Microcontrollers with a few KB of RAM still support a useful subset of C++.

Think twice before choosing C++ when

  • Team size and onboarding matter — C++ is one of the hardest languages to learn and review. Undefined behavior, ODR violations, and template error messages are notoriously opaque.
  • You need memory safety guarantees — C++ has smart pointers but no borrow checker. Rust offers comparable performance with provable safety. For new projects where safety is non-negotiable, Rust is the better bet.
  • Build times are a constraint — large C++ projects can take minutes to compile. C++ modules (C++20) help, but the ecosystem is still catching up.
  • You just need a fast scripting layer — Python with C extensions, or Go, will be far easier to maintain for network tools and data pipelines that don't need microsecond latency.

C++ vs. its closest alternatives

  • C++ vs C — C++ gives you RAII, OOP, templates, and exceptions at zero added runtime cost. Unless you need a tiny binary or strict POSIX C compliance, prefer C++.
  • C++ vs Rust — both give native performance. Rust prevents memory bugs at compile time; C++ gives you more flexibility (and more rope to hang yourself). New projects prioritising safety should lean Rust; large existing C++ codebases stay C++.
  • C++ vs Java/C# — Java and C# are far easier to write safely and have huge ecosystems. Choose them for enterprise applications; choose C++ when GC pauses or binary portability matter.

Resources

Topics

Variables & Types

cpp
#include <iostream>
#include <string>

int main() {
    // Fundamental types
    int       i  = 42;
    long      l  = 1234567890L;
    long long ll = 9999999999LL;
    float     f  = 3.14f;
    double    d  = 3.14159265;
    char      c  = 'A';
    bool      b  = true;

    // auto — type deduced by compiler
    auto x = 42;       // int
    auto y = 3.14;     // double
    auto z = 'A';      // char

    // const and constexpr
    const int MAX = 100;
    constexpr double PI = 3.14159265;

    // Uniform initialization (C++11)
    int arr[]{1, 2, 3};
    std::string str{'h', 'e', 'l', 'l', 'o'};

    std::cout << i << ' ' << d << '\n';
    return 0;
}

Initialization Forms

cpp
// C-style initialization
int a = 5;
double pi = 3.14;

// Copy initialization
std::vector<int> v1{1, 2, 3};
std::vector<int> v2 = v1;   // copy

// Direct initialization
std::vector<int> v3(5, 0);  // 5 zeros
std::string s1(3, 'x');     // 'xxx'

// Uniform / brace initialization (C++11)
int c{42};
int d{};                    // zero-initialize
std::vector<int> v4{1, 2, 3, 4};
std::pair<int, int> p{1, 2};

// Aggregate initialization
struct Point { int x; int y; };
Point pt{10, 20};           // members in declaration order

// Narrowing prevention — brace init rejects truncation
// int bad{3.14};           // error: narrowing conversion
int good = static_cast<int>(3.14);   // explicit cast required

// Designated initializers (C++20)
struct Config {
    int  width     = 800;
    int  height    = 600;
    bool fullscreen = false;
};
Config cfg{ .width = 1920, .height = 1080 };   // C++20
// unmentioned members keep their defaults

References

cpp
int x = 10;
int& ref = x;          // lvalue reference — alias for x
ref = 20;              // modifies x through the alias

const int& cref = x;  // const ref (read-only, no copy)

// Rvalue reference (C++11)
std::string s{'h', 'e', 'l', 'l', 'o'};
std::string&& rref = std::move(s);   // s moved-from  // C++11

// Pass by reference — avoids copy, allows mutation
void increment(int& val) { val++; }
increment(x);   // x becomes 21

// Pass by const reference — avoids copy, read-only
void print(const std::string& str) {
    std::cout << str << '\n';
}

// Returning a reference (ensure lifetime is valid)
int& get_elem(std::vector<int>& v, int i) { return v[i]; }

// Forwarding reference / universal reference (C++11)
template<typename T>
void forward_ex(T&& arg) {            // T&& is a forwarding reference
    consume(std::forward<T>(arg));    // preserves value category
}

Control Flow

cpp
// if with initializer (C++17)
if (int result = compute(); result > 0) {
    std::cout << 'positive: ' << result << '\n';
}                                              // C++17

// Range-based for loop (C++11)
std::vector<int> nums{1, 2, 3, 4, 5};
for (const auto& n : nums) {
    std::cout << n << ' ';
}

// Standard loops
for (int i = 0; i < 5; ++i) { /* ... */ }

int n = 5;
while (n-- > 0) { /* ... */ }

do { /* ... */ } while (false);

// switch with initializer (C++17)
switch (int code = get_code(); code) {
    case 200: std::cout << 'OK\n';       break;
    case 404: std::cout << 'Not found\n'; break;
    default:  std::cout << 'Other\n';    break;
}

// Scoped enum
enum class Direction { North, South, East, West };
Direction dir = Direction::North;
switch (dir) {
    case Direction::North: std::cout << 'N\n'; break;
    case Direction::South: std::cout << 'S\n'; break;
    default: break;
}

Functions

cpp
// Basic function
int add(int a, int b) { return a + b; }

// Default arguments
void log(const std::string& msg, int level = 1) {
    std::cout << '[' << level << "] " << msg << '\n';
}

// Trailing return type
auto multiply(int a, int b) -> int { return a * b; }

// Inline — hint to avoid call overhead
inline int square(int x) { return x * x; }

// constexpr — evaluated at compile time (C++11)
constexpr int factorial(int n) {            // C++11
    return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr int f6 = factorial(6);            // computed at compile time

// Function overloading
double area(double r);                      // circle
double area(double w, double h);            // rectangle

// std::function — type-erased callable
std::function<int(int, int)> op = add;
op(3, 4);   // 7

// Structured return (C++17)
auto divide(int a, int b) -> std::pair<int, int> {
    return {a / b, a % b};
}
auto [quot, rem] = divide(17, 5);           // C++17

Lambdas

cpp
#include <functional>

// Basic lambda (C++11)
auto square = [](int x) { return x * x; };   // C++11

// Capture by value [=] — copies all used locals
int offset = 10;
auto add_offset = [=](int x) { return x + offset; };   // C++11

// Capture by reference [&]
int count = 0;
auto inc = [&]() { ++count; };                          // C++11

// Capture specific variables
auto mixed = [offset, &count](int x) { count++; return x + offset; };

// Generic lambda (C++14)
auto println = [](auto x) { std::cout << x << '\n'; };   // C++14

// constexpr lambda (C++17)
auto sq_cx = [](int x) constexpr { return x * x; };       // C++17
constexpr int sq9 = sq_cx(9);   // evaluated at compile time

// Template lambda (C++20)
auto same_type = []<typename T>(T a, T b) { return a + b; };  // C++20

// Capture this
struct Counter {
    int value = 0;
    auto make_inc() {
        return [this]() { ++value; };   // captures this pointer
    }
};

// Immediately invoked lambda
int result = [](int a, int b) { return a + b; }(3, 4);   // 7

// Storing in std::function (type erasure)
std::function<int(int)> fn = [offset](int x) { return x + offset; };

Move Semantics

cpp
#include <utility>

// lvalue (has address) vs rvalue (temporary)
int x = 42;          // x is lvalue
int y = x + 1;       // (x + 1) is rvalue expression

// std::move — casts to rvalue reference, enabling move
std::vector<int> a{1, 2, 3};
std::vector<int> b = std::move(a);   // a is now empty  // C++11

// Move constructor and move assignment (C++11)
class Buffer {
    int*   data_;
    size_t size_;
public:
    explicit Buffer(size_t n) : data_(new int[n]), size_(n) {}
    ~Buffer() { delete[] data_; }

    // Copy constructor
    Buffer(const Buffer& o) : data_(new int[o.size_]), size_(o.size_) {
        std::copy(o.data_, o.data_ + size_, data_);
    }

    // Move constructor — transfer ownership, leave source empty
    Buffer(Buffer&& o) noexcept                           // C++11
        : data_(o.data_), size_(o.size_) {
        o.data_ = nullptr; o.size_ = 0;
    }

    // Move assignment
    Buffer& operator=(Buffer&& o) noexcept {              // C++11
        if (this != &o) {
            delete[] data_;
            data_ = o.data_; size_ = o.size_;
            o.data_ = nullptr; o.size_ = 0;
        }
        return *this;
    }
};

// Perfect forwarding — preserve lvalue/rvalue category
template<typename T>
void wrapper(T&& arg) {
    process(std::forward<T>(arg));   // C++11
}

Classes & OOP

cpp
class Animal {
    std::string sound_;
protected:
    std::string name_;
public:
    // Constructor with member-initializer list
    Animal(const std::string& name, const std::string& sound)
        : name_(name), sound_(sound) {}

    virtual ~Animal() = default;   // virtual destructor essential for polymorphism

    virtual std::string speak() const {
        return name_ + ' says ' + sound_;
    }

    // Rule of Five — define all or none (C++11)
    Animal(const Animal&)             = default;   // C++11
    Animal& operator=(const Animal&)  = default;
    Animal(Animal&&) noexcept         = default;
    Animal& operator=(Animal&&) noexcept = default;
};

class Dog : public Animal {
    std::string breed_;
public:
    Dog(const std::string& name, const std::string& breed)
        : Animal(name, 'Woof'), breed_(breed) {}

    std::string speak() const override {
        return Animal::speak() + '!';
    }
};

// Operator overloading
struct Vector2 {
    double x, y;
    Vector2 operator+(const Vector2& o) const { return {x+o.x, y+o.y}; }
    bool operator==(const Vector2&) const = default;   // C++20
};

Templates

cpp
// Function template — type deduced from arguments
template<typename T>
T max_val(T a, T b) { return (a > b) ? a : b; }

max_val(3, 7);        // T = int
max_val(1.5, 2.5);   // T = double

// Class template
template<typename T, std::size_t N>
class FixedArray {
    T data_[N]{};
public:
    T&       operator[](std::size_t i)       { return data_[i]; }
    const T& operator[](std::size_t i) const { return data_[i]; }
    constexpr std::size_t size() const { return N; }
};

FixedArray<int, 5> arr;
arr[0] = 42;

// Template specialization
template<typename T>
struct TypeName { static const char* name() { return 'unknown'; } };

template<>
struct TypeName<int> { static const char* name() { return 'int'; } };

// Non-type template parameters
template<int N>
constexpr int power_of_two = 1 << N;
static_assert(power_of_two<10> == 1024);

// Template aliases (C++11)
template<typename T>                        // C++11
using Vec = std::vector<T>;
Vec<int> v{1, 2, 3};

Variadic Templates

cpp
// Parameter pack (C++11)
template<typename... Args>
void print_all(Args&&... args) {            // C++11
    (std::cout << ... << args);             // fold expression (C++17)
}
print_all(1, ' ', 3.14, '\n');

// sizeof... — number of arguments in a pack
template<typename... Ts>
constexpr std::size_t count() { return sizeof...(Ts); }
static_assert(count<int, double, char>() == 3);

// Recursive variadic (C++11, pre-fold style)
void print_r() {}   // base case — ends recursion
template<typename T, typename... Rest>
void print_r(T first, Rest... rest) {        // C++11
    std::cout << first << ' ';
    print_r(rest...);   // peel one argument
}

// Fold expressions (C++17)
template<typename... Ns>
auto sum(Ns... ns) { return (0 + ... + ns); }    // left fold   // C++17

template<typename... Ns>
auto product(Ns... ns) { return (1 * ... * ns); }

template<typename... Ts>
bool all_true(Ts... bs) { return (... && bs); }  // unary left fold

// Forwarding a pack
template<typename T, typename... Args>
T create(Args&&... args) {
    return T(std::forward<Args>(args)...);   // perfect-forward each arg
}

// Expand into braced-init (pre-C++17 trick)
template<typename... Ts>
void process_all(Ts&&... args) {
    int dummy[] = { (process(std::forward<Ts>(args)), 0)... };
    (void)dummy;
}

Concepts (C++20)

cpp
#include <concepts>                          // C++20

// Define a concept — predicate evaluated at compile time
template<typename T>
concept Printable = requires(T x) {          // C++20
    std::cout << x;                          // must be valid expression
};

// requires expression — checks multiple operations
template<typename T>
concept Container = requires(T c) {
    c.begin();
    c.end();
    c.size();
    typename T::value_type;
};

// Concept with return-type constraint
template<typename T>
concept Hashable = requires(T v) {
    { std::hash<T>{}(v) } -> std::convertible_to<std::size_t>;
};

// Built-in standard concepts
template<std::integral T>
T double_it(T x) { return x * 2; }

template<std::floating_point T>
T half(T x) { return x / 2; }

// requires clause — inline constraint
template<typename T>
    requires std::integral<T> || std::floating_point<T>
T square(T x) { return x * x; }

// Constrained auto (abbreviated function template)
auto add(std::integral auto a, std::integral auto b) { return a + b; }
void print(Printable auto x) { std::cout << x << '\n'; }

// Combining concepts
template<typename T>
concept Number = std::integral<T> || std::floating_point<T>;

template<Number T>
T clamp(T val, T lo, T hi) { return val < lo ? lo : val > hi ? hi : val; }

Containers

cpp
#include <vector>
#include <array>
#include <deque>
#include <list>
#include <forward_list>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <queue>
#include <bitset>
#include <string_view>
#include <span>

// vector — dynamic contiguous array
std::vector<int> v{1, 2, 3};
v.push_back(4);
v.emplace_back(5);           // construct in-place
v.reserve(20);               // pre-allocate

// array — fixed-size, stack-allocated (C++11)
std::array<int, 5> arr{1, 2, 3, 4, 5};   // C++11
arr.fill(0);

// deque — double-ended queue, O(1) push/pop at both ends
std::deque<int> dq{2, 3, 4};
dq.push_front(1); dq.push_back(5);

// list — doubly-linked, O(1) insert/erase at iterator
std::list<int> lst{1, 3, 5};
lst.insert(std::next(lst.begin()), 2);   // {1, 2, 3, 5}
lst.sort();

// forward_list — singly-linked, minimal overhead (C++11)
std::forward_list<int> fl{3, 1, 4};   // C++11
fl.sort();

// map — sorted key-value, O(log n)
std::map<std::string, int> scores;
scores['Alice'] = 95;
scores.emplace('Bob', 87);
for (const auto& [key, val] : scores) {   // C++17
    std::cout << key << ':' << val << '\n';
}

// unordered_map — hash map, O(1) average
std::unordered_map<std::string, int> freq;
freq['apple']++;

// multimap / multiset — allow duplicate keys
std::multimap<int, int> mm;
mm.insert({1, 10}); mm.insert({1, 20});

// set / unordered_set
std::set<int> s{3, 1, 4, 1, 5};        // {1, 3, 4, 5}
std::unordered_set<int> us{1, 2, 3, 2}; // {1, 2, 3}
s.contains(4);   // C++20

// priority_queue — max-heap by default
std::priority_queue<int> pq;
pq.push(3); pq.push(1); pq.push(4);
int top = pq.top();   // 4

// bitset — fixed-size bit array
std::bitset<8> bits{0b10110010};
bits.set(0); bits.flip(3);
std::cout << bits.count() << '\n';

// string_view — non-owning string reference, no allocation (C++17)
std::string_view sv = 'hello';   // C++17
sv = sv.substr(1, 3);            // 'ell', zero-copy

// span — non-owning view of contiguous data (C++20)
std::span<int> sp{v};                   // C++20
std::span<int> first3 = sp.first(3);

Iterators

cpp
#include <iterator>
#include <sstream>

// Iterator categories:
// input < forward < bidirectional < random-access < contiguous

std::vector<int> v{1, 2, 3, 4, 5};

// begin / end
auto it  = v.begin();   // points to first element
auto end = v.end();     // one past last (sentinel)
std::cout << *it << '\n';   // 1

// Reverse iterators
for (auto rit = v.rbegin(); rit != v.rend(); ++rit)
    std::cout << *rit << ' ';   // 5 4 3 2 1

// Navigation helpers
auto it2  = std::next(v.begin(), 2);          // points to v[2]
auto it3  = std::prev(v.end(), 1);            // points to last
std::advance(it2, 1);                         // advance in-place by 1
auto dist = std::distance(v.begin(), it3);    // 4

// Iterator adaptors
std::vector<int> out;
std::copy(v.begin(), v.end(), std::back_inserter(out));   // push_back each

// std::istream_iterator
std::istringstream iss{'1', ' ', '2', ' ', '3'};
std::istream_iterator<int> start(iss), finish;
std::vector<int> parsed(start, finish);

// Custom forward iterator
struct IotaRange {
    int cur, last;
    IotaRange(int from, int to) : cur(from), last(to) {}
    IotaRange begin() const { return *this; }
    IotaRange end()   const { return {last, last}; }
    int  operator*()  const { return cur; }
    IotaRange& operator++()  { ++cur; return *this; }
    bool operator!=(const IotaRange& o) const { return cur != o.cur; }
};
for (int n : IotaRange{1, 6}) std::cout << n << ' ';   // 1 2 3 4 5

Algorithms

cpp
#include <algorithm>
#include <numeric>

std::vector<int> v{5, 3, 1, 4, 2, 3, 0};

// Non-modifying queries
auto it   = std::find(v.begin(), v.end(), 3);
int  cnt  = std::count(v.begin(), v.end(), 3);           // 2
bool any  = std::any_of (v.begin(), v.end(), [](int x){ return x > 4; });
bool all  = std::all_of (v.begin(), v.end(), [](int x){ return x >= 0; });
bool none = std::none_of(v.begin(), v.end(), [](int x){ return x < 0; });

// Modifying
std::transform(v.begin(), v.end(), v.begin(),
               [](int x){ return x * 2; });
std::fill(v.begin(), v.begin() + 3, 99);
std::replace(v.begin(), v.end(), 99, 0);
// erase-remove idiom
v.erase(std::remove_if(v.begin(), v.end(),
        [](int x){ return x % 2 == 0; }), v.end());

// Sorting
std::vector<int> w{5, 3, 1, 4, 2};
std::sort(w.begin(), w.end());                   // ascending
std::stable_sort(w.begin(), w.end(), std::greater<int>{});  // descending, stable
std::partial_sort(w.begin(), w.begin() + 3, w.end());  // smallest 3 in order
std::nth_element(w.begin(), w.begin() + 2, w.end());   // w[2] = median-ish

// Partitioning
std::partition(w.begin(), w.end(), [](int x){ return x % 2 == 0; });

// Reduction
int sum  = std::accumulate(w.begin(), w.end(), 0);
int prod = std::accumulate(w.begin(), w.end(), 1, std::multiplies<int>{});

// Binary search (sorted range required)
std::sort(w.begin(), w.end());
bool found = std::binary_search(w.begin(), w.end(), 3);
auto lb    = std::lower_bound(w.begin(), w.end(), 3);

// Merge / set operations
std::vector<int> a{1,2,3}, b{2,3,4}, out;
std::merge(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(out));
out.clear();
std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(out));
std::set_union(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(out));

Ranges (C++20)

cpp
#include <ranges>                               // C++20

std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};

// View adaptors — lazy, no allocation
auto evens  = v | std::views::filter([](int x){ return x % 2 == 0; });
auto sq     = v | std::views::transform([](int x){ return x * x; });
auto first3 = v | std::views::take(3);
auto skip2  = v | std::views::drop(2);
auto rev    = v | std::views::reverse;

// Chained pipe — evaluated lazily on iteration
auto result = v
    | std::views::filter([](int x){ return x % 2 == 0; })
    | std::views::transform([](int x){ return x * x; })
    | std::views::take(3);
for (int n : result) std::cout << n << ' ';   // 4 16 36

// std::ranges algorithms — accept range directly
std::ranges::sort(v);
auto it = std::ranges::find(v, 4);
std::vector<int> out;
std::ranges::copy(v, std::back_inserter(out));

// Sentinels — decoupled begin/end types
auto positive = v | std::views::take_while([](int x){ return x < 5; });

// std::views::iota — integer range
for (int n : std::views::iota(1, 6)) std::cout << n << ' ';  // 1 2 3 4 5

// std::views::zip (C++23)
std::vector<int>  nums{'1', '2', '3'};
std::vector<char> chars{'a', 'b', 'c'};
for (auto [n, c] : std::views::zip(nums, chars))   // C++23
    std::cout << n << ':' << c << ' ';

// std::views::enumerate (C++23)
for (auto [i, val] : std::views::enumerate(v))     // C++23
    std::cout << i << '=' << val << ' ';

Structured Bindings (C++17)

cpp
#include <map>
#include <tuple>                              // C++17 throughout

// Array decomposition
int arr[]{10, 20, 30};
auto [a, b, c] = arr;                        // C++17

// std::pair
auto p = std::make_pair(42, 3.14);
auto [key, val] = p;

// std::tuple
auto t = std::make_tuple(1, 2.5, 'x');
auto [i, d, ch] = t;

// Struct — public non-static data members
struct Point3D { int x; int y; int z; };
Point3D pt{1, 2, 3};
auto [x, y, z] = pt;

// Range-for over std::map (most common use)
std::map<int, int> squares{{1,1},{2,4},{3,9}};
for (const auto& [k, v] : squares)
    std::cout << k << ':' << v << '\n';

// Mutable bindings — modify in-place
for (auto& [k, v] : squares) v *= 2;

// const auto& — read-only access to first element
const auto& [fk, fv] = *squares.begin();

// Ignore a member with structured binding
auto [first, second] = std::make_pair(1, 2);
// use only 'first', ignore 'second' (or use [[maybe_unused]])

// [[nodiscard]] with structured return (C++17)
[[nodiscard]] std::pair<bool, int> try_parse(int raw);
auto [ok, result] = try_parse(42);
if (ok) std::cout << result;

optional & variant (C++17)

cpp
#include <optional>
#include <variant>                           // C++17 throughout

// std::optional — value that may or may not exist
std::optional<int> maybe;                   // empty
maybe = 42;                                 // assign
maybe.emplace(99);                          // construct in-place

if (maybe.has_value())
    std::cout << *maybe << '\n';            // dereference
std::cout << maybe.value_or(0) << '\n';    // safe access with default

maybe.reset();                              // back to empty

// Optional return pattern — cleaner than out-params or error codes
std::optional<int> divide(int a, int b) {
    if (b == 0) return std::nullopt;
    return a / b;
}
if (auto r = divide(10, 2)) std::cout << *r;

// std::variant — type-safe union
std::variant<int, double, std::string> var = 42;
var = 3.14;                                 // reassign different type

// Access with std::get / std::get_if
if (std::holds_alternative<double>(var))
    std::cout << std::get<double>(var) << '\n';

if (auto* p = std::get_if<int>(&var))
    std::cout << *p;

// std::visit — apply visitor to active member
std::visit([](auto&& v){ std::cout << v << '\n'; }, var);

// Overloaded visitor pattern
struct Visitor {
    void operator()(int i)         const { std::cout << 'int:' << i; }
    void operator()(double d)      const { std::cout << 'dbl:' << d; }
    void operator()(std::string s) const { std::cout << 'str:' << s; }
};
std::visit(Visitor{}, var);

expected (C++23)

cpp
#include <expected>                          // C++23

// std::expected<T, E> — value or error, no exception overhead
std::expected<int, std::string> parse_int(const std::string& s) {
    try {
        return std::stoi(s);
    } catch (...) {
        return std::unexpected('not a number');
    }
}

// Check and access
auto r = parse_int('42');
if (r.has_value())
    std::cout << 'value: ' << r.value() << '\n';
else
    std::cout << 'error: ' << r.error() << '\n';

// Concise access with value_or
int n = parse_int('abc').value_or(-1);   // -1 on error

// Monadic operations (C++23)
auto result = parse_int('5')
    .and_then([](int v) -> std::expected<int, std::string> {
        if (v < 0) return std::unexpected('negative');
        return v * 2;
    })
    .transform([](int v){ return v + 1; })
    .or_else([](auto&&) -> std::expected<int, std::string> {
        return 0;   // default on error
    });

// vs exceptions pattern
std::expected<int, int> safe_div(int a, int b) {   // C++23
    if (b == 0) return std::unexpected(-1);         // error code -1
    return a / b;
}

// Chain without try/catch
safe_div(10, 2)
    .transform([](int v){ return v * 3; })
    .and_then([](int v) -> std::expected<int, int> { return v; });

Memory & Smart Pointers

cpp
#include <memory>

// unique_ptr — sole ownership, zero overhead
auto p = std::make_unique<int>(42);
auto p2 = std::move(p);   // transfer ownership; p is now nullptr

// Custom deleter
auto file_ptr = std::unique_ptr<FILE, decltype(&fclose)>(
    fopen('data.bin', 'rb'), &fclose
);

// shared_ptr — shared ownership, ref-counted
auto sp1 = std::make_shared<std::string>(3, 'x');
auto sp2 = sp1;           // ref count = 2
sp1.use_count();          // 2

// weak_ptr — non-owning observer, breaks cycles
std::weak_ptr<std::string> wp = sp1;
if (auto locked = wp.lock()) {   // safe: lock() returns nullptr if expired
    std::cout << *locked << '\n';
}

// Stack vs heap
std::vector<int> v(100);        // heap storage, stack handle (RAII)
std::array<int, 100> arr{};     // fixed-size on stack

// RAII wrapper pattern
class FileHandle {
    FILE* f_;
public:
    explicit FileHandle(const char* path) : f_(fopen(path, 'r')) {}
    ~FileHandle() { if (f_) fclose(f_); }
    FILE* get() const { return f_; }
};

Exceptions

cpp
#include <stdexcept>

// try / catch hierarchy — most-derived first
try {
    throw std::runtime_error('something went wrong');
} catch (const std::invalid_argument& e) {
    std::cerr << 'Invalid: ' << e.what() << '\n';
} catch (const std::out_of_range& e) {
    std::cerr << 'Range: '   << e.what() << '\n';
} catch (const std::exception& e) {
    std::cerr << 'Error: '   << e.what() << '\n';
} catch (...) {
    std::cerr << 'Unknown error\n';
}

// Custom exception
class AppError : public std::runtime_error {
    int code_;
public:
    AppError(const std::string& msg, int code)
        : std::runtime_error(msg), code_(code) {}
    int code() const { return code_; }
};

throw AppError('not found', 404);

// noexcept — declares function will not throw
void safe_swap(int& a, int& b) noexcept {
    int tmp = a; a = b; b = tmp;
}

// noexcept(expr) — conditional noexcept
template<typename T>
void move_if_possible(T& a, T& b) noexcept(std::is_nothrow_move_constructible_v<T>) {
    T tmp = std::move(a); a = std::move(b); b = std::move(tmp);
}

// RAII — cleanup guaranteed via destructor (never leaks on exception)
class TxGuard {
    DB& db_;
    bool committed_ = false;
public:
    TxGuard(DB& db) : db_(db) { db_.begin(); }
    void commit() { db_.commit(); committed_ = true; }
    ~TxGuard() { if (!committed_) db_.rollback(); }
};

Type Traits & SFINAE

The <type_traits> header provides compile-time predicates and type transformations. SFINAE (via enable_if) and if constexpr let you branch on type properties at compile time — concepts (C++20) replace most SFINAE use cases with cleaner syntax.

cpp
#include <type_traits>

// Type queries — evaluate to true/false at compile time
static_assert(std::is_integral_v<int>);
static_assert(std::is_floating_point_v<double>);
static_assert(!std::is_pointer_v<int>);
static_assert(std::is_same_v<int, int>);

// Type transformations
using T1 = std::remove_const_t<const int>;        // int
using T2 = std::add_pointer_t<int>;               // int*
using T3 = std::decay_t<int[3]>;                  // int*
using T4 = std::conditional_t<true, int, double>; // int
using T5 = std::common_type_t<int, double>;       // double

// SFINAE via enable_if (pre-C++20)
template<typename T>
std::enable_if_t<std::is_arithmetic_v<T>, T>
square(T x) { return x * x; }

// if constexpr — cleaner branch-at-compile-time
template<typename T>
std::string to_str(T val) {
    if constexpr (std::is_arithmetic_v<T>)
        return std::to_string(val);
    else
        return std::string(val);
}

// Tag dispatch — select overload by iterator category
template<typename Iter>
void advance_impl(Iter& it, int n, std::random_access_iterator_tag) {
    it += n;                // O(1)
}
template<typename Iter>
void advance_impl(Iter& it, int n, std::input_iterator_tag) {
    while (n--) ++it;      // O(n)
}
template<typename Iter>
void my_advance(Iter& it, int n) {
    using Cat = typename std::iterator_traits<Iter>::iterator_category;
    advance_impl(it, n, Cat{});
}

Fold Expressions (C++17)

Fold expressions collapse a parameter pack into a single value using a binary operator. They supersede the recursive variadic template idiom for arithmetic reductions and function application.

cpp
// C++17 — reduce a parameter pack with a binary operator
// (pack op ...)    — right fold
// (... op pack)    — left fold
// (pack op ... op init)  — right fold with init
// (init op ... op pack)  — left fold with init

template<typename... Ts>
auto sum(Ts... args) {
    return (args + ...);          // left fold:  ((a + b) + c) + d
}

template<typename... Ts>
auto product(Ts... args) {
    return (... * args);
}

// && / || fold — short-circuit preserved
template<typename... Ts>
bool all_positive(Ts... args) { return ((args > 0) && ...); }

template<typename... Ts>
bool any_zero(Ts... args)     { return ((args == 0) || ...); }

// Comma fold — invoke a callable for each element
template<typename F, typename... Ts>
void for_each_arg(F f, Ts&&... args) {
    (f(std::forward<Ts>(args)), ...);
}

// << fold — variadic print
template<typename... Ts>
void print(Ts&&... args) {
    (std::cout << ... << args);
    std::cout << '\n';
}

// Sizeof... — count pack elements at compile time
template<typename... Ts>
constexpr std::size_t count() { return sizeof...(Ts); }

// Usage
sum(1, 2, 3, 4, 5);           // 15
product(2, 3, 4);             // 24
all_positive(1, 2, 3);        // true
print('hello', ' ', 42);      // hello 42

Concurrency

The standard threading library (<thread>, <mutex>, <atomic>, <future>) covers most concurrency needs. std::jthread (C++20) adds cooperative cancellation via stop tokens and automatic joining.

cpp
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <future>
#include <vector>

// std::atomic — lock-free shared state
std::atomic<int> counter{0};
void increment() {
    for (int i = 0; i < 100'000; ++i)
        counter.fetch_add(1, std::memory_order_relaxed);
}

// std::mutex + lock_guard (RAII)
std::mutex mtx;
std::vector<int> results;
void worker(int id) {
    std::lock_guard lock{mtx};   // C++17 CTAD
    results.push_back(id);
}

// condition_variable — notify between threads
std::condition_variable cv;
bool ready = false;
void producer() {
    { std::lock_guard lk{mtx}; ready = true; }
    cv.notify_all();
}
void consumer() {
    std::unique_lock lk{mtx};
    cv.wait(lk, []{ return ready; });   // predicate form — no spurious wakes
}

// std::async + future — fire-and-get
auto fut = std::async(std::launch::async, []{ return 42; });
int result = fut.get();   // blocks until ready

// std::jthread (C++20) — auto-joins, has stop token
#include <stop_token>
std::jthread bg([](std::stop_token tok) {
    while (!tok.stop_requested()) { /* work */ }
});
bg.request_stop();   // or destructor stops it automatically

// Launch N threads
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i) threads.emplace_back(increment);
for (auto& t : threads) t.join();

Coroutines (C++20)

Coroutines are functions that can suspend and resume execution. co_yield suspends and produces a value; co_await suspends until an awaitable is ready. The compiler transforms a coroutine body into a state machine stored on a heap-allocated frame.

cpp
#include <coroutine>
#include <optional>

// C++20 — Generator that lazily yields values
template<typename T>
struct Generator {
    struct promise_type {
        T current_value;
        std::suspend_always yield_value(T v) { current_value = v; return {}; }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        Generator get_return_object() {
            return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
        }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };

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

    std::optional<T> next() {
        if (handle.done()) return std::nullopt;
        handle.resume();
        if (handle.done()) return std::nullopt;
        return handle.promise().current_value;
    }
};

Generator<int> fibonacci() {
    int a = 0, b = 1;
    while (true) {
        co_yield a;           // suspend here, save {a, b} on coroutine frame
        auto tmp = a + b;
        a = b;
        b = tmp;
    }
}

Generator<int> range(int from, int to) {
    for (int i = from; i < to; ++i)
        co_yield i;
}

// Caller drives execution — no OS thread needed
auto gen = fibonacci();
for (int i = 0; i < 10; ++i)
    std::cout << gen.next().value() << ' ';
// 0 1 1 2 3 5 8 13 21 34

std::format & Chrono (C++20)

std::format provides Python-style format strings with compile-time format-string checking (C++23). <chrono> offers type-safe duration arithmetic and calendar types.

cpp
#include <format>
#include <chrono>
#include <string>
using namespace std::chrono;
using namespace std::chrono_literals;

// std::format (C++20) — type-safe, extensible printf replacement
std::string s1 = std::format('Hello, {}!', 'world');
std::string s2 = std::format('{:>10}',  'right');      // right-align in 10
std::string s3 = std::format('{:.2f}',  3.14159);      // '3.14'
std::string s4 = std::format('{:#010x}', 255);         // '0x000000ff'
std::string s5 = std::format('{0} {1} {0}', 'A', 'B'); // 'A B A' by index

// std::println (C++23) — format + write + newline
// std::println('Answer: {}', 42);

// Chrono — durations and clocks (C++11, extended in C++20)
auto elapsed = 1h + 30min + 45s;
auto ms = duration_cast<milliseconds>(elapsed);
std::cout << ms.count() << ' ms\n';   // 5445000 ms

// Measure wall time
auto t0 = steady_clock::now();
// ... work ...
auto dt = duration_cast<microseconds>(steady_clock::now() - t0);
std::cout << dt.count() << ' us\n';

// Calendar types (C++20)
auto today = year_month_day{floor<days>(system_clock::now())};
std::cout << std::format('{}\n', today);   // 2024-07-15

// Custom formatter — implement std::formatter<T>
struct Point { int x, y; };
template<>
struct std::formatter<Point> {
    constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
    auto format(const Point& p, format_context& ctx) const {
        return std::format_to(ctx.out(), '({}, {})', p.x, p.y);
    }
};
std::string s = std::format('{}', Point{3, 4});  // '(3, 4)'

std::filesystem (C++17)

std::filesystem provides portable path manipulation, directory traversal, and file operations. Use std::error_code overloads to avoid exceptions in performance-sensitive or recoverable-error paths.

cpp
#include <filesystem>
#include <fstream>
namespace fs = std::filesystem;

// Path manipulation (C++17)
fs::path p = '/home/user/docs/report.pdf';
auto parent = p.parent_path();   // /home/user/docs
auto name   = p.filename();      // report.pdf
auto stem   = p.stem();          // report
auto ext    = p.extension();     // .pdf
auto full   = p / 'backup';      // append component

// File queries
bool ex  = fs::exists(p);
bool reg = fs::is_regular_file(p);
bool dir = fs::is_directory(p);
auto sz  = fs::file_size(p);
auto mtime = fs::last_write_time(p);

// Directory listing — flat
for (const auto& entry : fs::directory_iterator('/tmp')) {
    if (entry.is_regular_file())
        std::cout << entry.path().filename() << '\n';
}

// Recursive listing — filter by extension
for (const auto& entry : fs::recursive_directory_iterator('.')) {
    if (entry.path().extension() == '.cpp')
        std::cout << entry.path().string() << '\n';
}

// Filesystem operations
fs::create_directories('a/b/c');           // like mkdir -p
fs::copy('src.txt', 'dst.txt');
fs::rename('old.txt', 'new.txt');
fs::remove('file.txt');
fs::remove_all('dir/');                    // recursive delete

// Error handling without exceptions
std::error_code ec;
fs::remove_all('dir/', ec);
if (ec) std::cerr << ec.message() << '\n';

// Temp path
auto tmp = fs::temp_directory_path() / 'scratch.bin';

RAII & Resource Management

RAII (Resource Acquisition Is Initialization) ties resource lifetime to object lifetime: acquire in the constructor, release in the destructor. This guarantees cleanup on any exit path — return, exception, or scope end — and is the foundation of every standard library resource type (unique_ptr, lock_guard, fstream).

cpp
// RAII: acquire resource in constructor, release in destructor
// Guarantees cleanup even on exceptions or early returns

#include <cstdio>
#include <mutex>
#include <memory>

// File RAII wrapper — no leak regardless of exit path
class File {
    FILE* handle_ = nullptr;
public:
    explicit File(const char* path, const char* mode)
        : handle_(std::fopen(path, mode)) {
        if (!handle_) throw std::runtime_error('cannot open file');
    }
    ~File() { if (handle_) std::fclose(handle_); }

    // Non-copyable: two owners would double-close
    File(const File&)            = delete;
    File& operator=(const File&) = delete;

    // Movable: transfer sole ownership
    File(File&& o) noexcept : handle_(o.handle_) { o.handle_ = nullptr; }
    File& operator=(File&& o) noexcept {
        if (this != &o) { if (handle_) std::fclose(handle_); handle_ = o.handle_; o.handle_ = nullptr; }
        return *this;
    }

    FILE* get() const { return handle_; }
    bool  ok()  const { return handle_ != nullptr; }
};

// Mutex RAII: lock_guard / unique_lock
std::mutex mtx;
void thread_safe_work() {
    std::lock_guard<std::mutex> guard(mtx);   // locked here
    // ...
}   // unlocked here — even on exception

// ScopeGuard: run arbitrary cleanup at scope exit
template<typename F>
class ScopeGuard {
    F fn_;
    bool active_ = true;
public:
    explicit ScopeGuard(F f) : fn_(std::move(f)) {}
    ~ScopeGuard() { if (active_) fn_(); }
    void dismiss() { active_ = false; }

    ScopeGuard(const ScopeGuard&)            = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;
};
template<typename F> ScopeGuard<F> make_scope_guard(F f) { return ScopeGuard<F>{std::move(f)}; }

// Usage
void with_temp_file() {
    auto guard = make_scope_guard([]{ std::remove('tmp.bin'); });
    // ... write to tmp.bin ...
    // guard fires on any exit path: return, throw, or fall-through
}

// Smart pointers = RAII in the standard library
auto p   = std::make_unique<int>(42);    // unique ownership
auto sp1 = std::make_shared<int>(99);    // shared ownership
auto sp2 = sp1;                          // ref-count 2
// freed automatically when all owners go out of scope

Operator Overloading

Operator overloading lets user-defined types participate in expressions with natural syntax. Member operators receive the left operand implicitly; non-member (friend) operators are needed when the left operand is not your type. C++20's spaceship operator <=> auto-generates all six comparison operators from a single definition.

cpp
#include <iostream>
#include <compare>

// Operator overloading: give user-defined types natural syntax
struct Vec2 {
    double x, y;

    // Arithmetic
    Vec2 operator+(const Vec2& o) const { return {x + o.x, y + o.y}; }
    Vec2 operator-(const Vec2& o) const { return {x - o.x, y - o.y}; }
    Vec2 operator*(double s)      const { return {x * s,   y * s};   }
    Vec2& operator+=(const Vec2& o) { x += o.x; y += o.y; return *this; }

    // Unary minus
    Vec2 operator-() const { return {-x, -y}; }

    // Spaceship operator — auto-generates all six comparisons (C++20)
    auto operator<=>(const Vec2&) const = default;   // C++20
    bool operator==(const Vec2&)  const = default;

    // Stream output — free function to allow cout on the left
    friend std::ostream& operator<<(std::ostream& os, const Vec2& v) {
        return os << '(' << v.x << ',' << v.y << ')';
    }
};

// Non-member scalar multiplication: s * v
Vec2 operator*(double s, const Vec2& v) { return v * s; }

// Subscript operator
struct Matrix2x2 {
    double data[2][2]{};
    double* operator[](int i)             { return data[i]; }
    const double* operator[](int i) const { return data[i]; }
};

// Call operator — makes objects callable (functor / closure alternative)
struct Adder {
    int base;
    int operator()(int x) const { return base + x; }
};
Adder add5{5};
int result = add5(10);   // 15

// Conversion operator
struct Celsius {
    double deg;
    explicit operator double() const { return deg; }   // explicit prevents implicit conversion
};

// Usage
Vec2 a{1, 2}, b{3, 4};
Vec2 c = a + b;                // {4, 6}
Vec2 d = 2.0 * a;             // {2, 4}
std::cout << c << '\n';       // (4,6)
bool eq = (a == a);           // true

Virtual Dispatch & vtable

Virtual dispatch implements runtime polymorphism: the compiler builds a vtable (a per-class array of function pointers) and stores a hidden vptr in each polymorphic object. Calling a virtual function follows the vptr to the vtable and then calls the correct override. Marking a class final or a call final lets the compiler devirtualize when the type is known.

cpp
// Virtual dispatch: runtime selection of the correct overridden method
// Implemented via a vtable (array of function pointers) per class type.
// Each polymorphic object stores a hidden vptr pointing at its class vtable.

#include <memory>
#include <vector>

class Shape {
public:
    virtual ~Shape() = default;             // MUST be virtual for correct delete

    virtual double area()     const = 0;   // pure virtual — Shape is abstract
    virtual double perimeter() const = 0;
    virtual std::string name() const { return 'Shape'; }   // optional override
};

class Circle : public Shape {
    double r_;
public:
    explicit Circle(double r) : r_(r) {}
    double area()      const override { return 3.14159265 * r_ * r_; }
    double perimeter() const override { return 2 * 3.14159265 * r_; }
    std::string name() const override { return 'Circle'; }
};

class Rect : public Shape {
    double w_, h_;
public:
    Rect(double w, double h) : w_(w), h_(h) {}
    double area()      const override { return w_ * h_; }
    double perimeter() const override { return 2 * (w_ + h_); }
    std::string name() const override { return 'Rect'; }
};

// Polymorphic container: store by pointer-to-base
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(5.0));
shapes.push_back(std::make_unique<Rect>(3.0, 4.0));

for (const auto& s : shapes)
    std::cout << s->name() << ' area=' << s->area() << '\n';
// Correct virtual dispatch — no switch, no cast

// final: prevent further overriding / inheritance
class Square final : public Rect {
public:
    explicit Square(double side) : Rect(side, side) {}
    // cannot be subclassed (compiler can devirtualize calls)
};

// Checking dynamic type at runtime
Shape* p = shapes[0].get();
if (auto* c = dynamic_cast<Circle*>(p)) {
    std::cout << 'Is a circle, r=' << c->area() << '\n';
}

// Non-virtual interface (NVI) pattern: public non-virtual, private virtual
class Logger {
public:
    void log(const std::string& msg) { do_log('[LOG] ' + msg); }   // non-virtual
private:
    virtual void do_log(const std::string& msg) { std::cout << msg << '\n'; }
};

Type Erasure

Type erasure hides a concrete type behind a uniform interface, enabling heterogeneous collections without a common base class. The standard library uses it in std::function, std::any, and std::span. The virtual concept/model idiom replicates this pattern for custom value-semantic wrappers.

cpp
// Type erasure: store objects of different types behind a uniform interface
// without exposing concrete types to callers.
// std::function, std::any, std::span are standard examples.

#include <functional>
#include <any>
#include <memory>
#include <vector>

// --- std::function: type-erases any callable ---
std::function<int(int, int)> op;
op = [](int a, int b){ return a + b; };
op = std::plus<int>{};            // function object
op(3, 4);                         // 7

// --- std::any: type-erases any CopyConstructible value ---
#include <any>
std::any a = 42;
a = std::string('hello');
std::cout << std::any_cast<std::string>(a) << '\n';
if (auto* p = std::any_cast<int>(&a)) std::cout << *p;   // null if wrong type

// --- Manual type erasure via virtual concept/model idiom ---
// Erase: all Drawable types, no inheritance required
struct DrawConcept {
    virtual ~DrawConcept() = default;
    virtual void draw() const = 0;
    virtual std::unique_ptr<DrawConcept> clone() const = 0;
};

template<typename T>
struct DrawModel : DrawConcept {
    T obj;
    explicit DrawModel(T o) : obj(std::move(o)) {}
    void draw() const override { obj.draw(); }
    std::unique_ptr<DrawConcept> clone() const override {
        return std::make_unique<DrawModel<T>>(obj);
    }
};

class Drawable {
    std::unique_ptr<DrawConcept> concept_;
public:
    template<typename T>
    Drawable(T obj) : concept_(std::make_unique<DrawModel<T>>(std::move(obj))) {}
    Drawable(const Drawable& o) : concept_(o.concept_->clone()) {}
    void draw() const { concept_->draw(); }
};

// Concrete types — no common base class needed
struct Circle { double r; void draw() const { std::cout << 'O\n'; } };
struct Star   { int pts; void draw() const { std::cout << '*\n'; } };

std::vector<Drawable> scene;
scene.emplace_back(Circle{5.0});
scene.emplace_back(Star{6});
for (const auto& d : scene) d.draw();

CRTP (C++11)

CRTP (Curiously Recurring Template Pattern) has a base class template accept its derived class as a type parameter. The base can call derived methods via static_cast without virtual dispatch, enabling zero-overhead static polymorphism, mixin composition, and per-class counters.

cpp
// CRTP: Curiously Recurring Template Pattern
// Base<Derived> accepts the derived class as a template argument,
// enabling static (compile-time) polymorphism with zero virtual overhead.

#include <iostream>
#include <cmath>

// Base provides shared interface, calls into Derived via static_cast
template<typename Derived>
class Shape {
public:
    // CRTP dispatch — no virtual, resolved at compile time
    double area()      const { return static_cast<const Derived*>(this)->area_impl(); }
    double perimeter() const { return static_cast<const Derived*>(this)->perimeter_impl(); }

    // Shared implementation built on the customisation points
    bool larger_than(const Shape& other) const {
        return area() > other.area();
    }
    void print() const {
        std::cout << 'area=' << area() << ' peri=' << perimeter() << '\n';
    }
};

class Circle : public Shape<Circle> {
    double r_;
public:
    explicit Circle(double r) : r_(r) {}
    double area_impl()      const { return 3.14159 * r_ * r_; }
    double perimeter_impl() const { return 2 * 3.14159 * r_; }
};

class Rect : public Shape<Rect> {
    double w_, h_;
public:
    Rect(double w, double h) : w_(w), h_(h) {}
    double area_impl()      const { return w_ * h_; }
    double perimeter_impl() const { return 2 * (w_ + h_); }
};

// CRTP mixin: add clone() to any class that provides copy construction
template<typename Derived>
class Cloneable {
public:
    std::unique_ptr<Derived> clone() const {
        return std::make_unique<Derived>(static_cast<const Derived&>(*this));
    }
};

class Widget : public Cloneable<Widget> {
public:
    int id;
    explicit Widget(int i) : id(i) {}
};
auto w1 = Widget{42};
auto w2 = w1.clone();   // std::unique_ptr<Widget>

// CRTP counter: count instances per concrete type
template<typename T>
class Counter {
    inline static int count_ = 0;
public:
    Counter()  { ++count_; }
    ~Counter() { --count_; }
    static int count() { return count_; }
};

class Dog : public Counter<Dog> {};
class Cat : public Counter<Cat> {};
Dog d1, d2;
Cat c1;
std::cout << Dog::count() << '\n';   // 2
std::cout << Cat::count() << '\n';   // 1

Policy-Based Design

Policy-based design composes behaviour from interchangeable template-parameter classes called policies. Each policy governs one orthogonal concern (storage, threading, formatting). Because policies are resolved at compile time, the resulting class has no virtual overhead and the optimizer sees the full implementation.

cpp
// Policy-based design: compose behaviour from interchangeable policy classes.
// Policies are template parameters — selection is resolved at compile time,
// producing zero overhead compared to runtime polymorphism.

#include <iostream>
#include <fstream>
#include <string>
#include <mutex>

// --- Storage policy ---
struct HeapStorage {
    static char* alloc(std::size_t n) { return new char[n]; }
    static void  free(char* p)        { delete[] p; }
};
struct StackStorage {
    static char* alloc(std::size_t) { return nullptr; } // simplified
    static void  free(char*)        {}
};

// --- Locking policy ---
struct SingleThreaded {
    struct Lock {};
    Lock acquire() { return {}; }
};
struct MultiThreaded {
    std::mutex m;
    std::unique_lock<std::mutex> acquire() { return std::unique_lock<std::mutex>(m); }
};

// --- Logger: accepts output and formatting policies ---
struct ConsoleOutput {
    static void write(const std::string& msg) { std::cout << msg; }
};
struct FileOutput {
    std::ofstream fs;
    FileOutput() : fs('app.log', std::ios::app) {}
    void write(const std::string& msg) { fs << msg; }
};

struct PlainFormat {
    static std::string format(const std::string& msg) { return msg + '\n'; }
};
struct PrefixFormat {
    static std::string format(const std::string& msg) { return '[LOG] ' + msg + '\n'; }
};

template<typename OutputPolicy  = ConsoleOutput,
         typename FormatPolicy  = PlainFormat,
         typename ThreadPolicy  = SingleThreaded>
class Logger : private OutputPolicy,
               private FormatPolicy,
               private ThreadPolicy {
public:
    void log(const std::string& msg) {
        auto lock = ThreadPolicy::acquire();
        OutputPolicy::write(FormatPolicy::format(msg));
    }
};

// Zero-cost instantiation — all policies inlined
Logger<>                                    plain_logger;
Logger<ConsoleOutput, PrefixFormat>         prefixed;
Logger<ConsoleOutput, PlainFormat, MultiThreaded> thread_safe;

plain_logger.log('hello');     // hello
prefixed.log('world');         // [LOG] world

stringstream & string_view

std::stringstream provides in-memory text serialization and parsing using the familiar stream interface. std::string_view (C++17) is a non-owning reference to a contiguous character sequence — it accepts any string-like source with zero allocation. std::from_chars / std::to_chars are the fastest locale-independent number conversions.

cpp
#include <sstream>
#include <string>
#include <string_view>
#include <charconv>   // C++17 from_chars / to_chars

// --- std::ostringstream: build strings efficiently ---
std::ostringstream oss;
oss << 'x=' << 42 << ' y=' << 3.14;
std::string result = oss.str();   // 'x=42 y=3.14'

// --- std::istringstream: parse whitespace-separated tokens ---
std::istringstream iss('10 20 30 40');
int n;
std::vector<int> nums;
while (iss >> n) nums.push_back(n);   // {10, 20, 30, 40}

// --- std::stringstream: read and write ---
std::stringstream ss;
ss << 100 << ' ' << 200;
int a, b;
ss >> a >> b;   // a=100, b=200

// --- std::string_view: non-owning, zero-copy string reference (C++17) ---
std::string_view sv = 'hello world';
std::string_view sub = sv.substr(6, 5);   // 'world' — no allocation
bool starts = sv.starts_with('hello');    // true  (C++20)
bool ends   = sv.ends_with('world');      // true  (C++20)
std::size_t pos = sv.find('world');       // 6

// string_view as function parameter: accepts string, literal, span, all zero-copy
void print_sv(std::string_view sv) {
    for (char c : sv) std::cout << c;
    std::cout << '\n';
}
print_sv('literal');
print_sv(std::string('dynamic'));

// --- std::from_chars / std::to_chars: fastest text<->number (C++17) ---
char buf[32];
auto [ptr, ec] = std::to_chars(buf, buf + sizeof(buf), 12345);
// ec == std::errc{} on success

int val = 0;
std::string_view input = '9876';
auto [p2, ec2] = std::from_chars(input.data(), input.data() + input.size(), val);
// val == 9876, ec2 == std::errc{}

// Trim leading/trailing whitespace using string_view (zero-copy)
std::string_view trim(std::string_view sv) {
    while (!sv.empty() && sv.front() == ' ') sv.remove_prefix(1);
    while (!sv.empty() && sv.back()  == ' ') sv.remove_suffix(1);
    return sv;
}

std::regex

std::regex provides POSIX-style regular expressions. Use raw string literals (R"(...)") to avoid double-escaping backslashes. Compile the regex once (construction is expensive) and reuse the object. For performance-critical code prefer std::string::find or SIMD-based libraries; std::regex prioritises correctness and portability.

cpp
#include <regex>
#include <iostream>
#include <string>
#include <iterator>

// std::regex: POSIX-style regular expressions in the standard library

// Basic match — does the entire string match?
std::regex ipv4(R'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})');
std::string addr = '192.168.1.1';
bool ok = std::regex_match(addr, ipv4);   // true

// Search — find first match anywhere in string
std::regex word_re(R'(\b\w{4}\b)');
std::string text = 'the quick brown fox';
std::smatch m;
if (std::regex_search(text, m, word_re)) {
    std::cout << 'found: ' << m[0] << '\n';   // 'this' or 'quick'
}

// Capture groups
std::regex date_re(R'((d{4})-(d{2})-(d{2}))');
std::string s = 'date: 2024-07-15';
if (std::regex_search(s, m, date_re)) {
    std::cout << 'year='  << m[1] << '\n';
    std::cout << 'month=' << m[2] << '\n';
    std::cout << 'day='   << m[3] << '\n';
}

// Iterate all matches in a string
std::string src = 'foo 42 bar 99 baz 7';
std::regex num_re(R'(d+)');
std::sregex_iterator it(src.begin(), src.end(), num_re);
std::sregex_iterator end;
for (; it != end; ++it)
    std::cout << (*it)[0] << ' ';   // 42 99 7

// Replace matches
std::regex vowels(R'([aeiou])');
std::string result = std::regex_replace(src, vowels, '*');

// Case-insensitive flag
std::regex ci_re(R'(hello)', std::regex_constants::icase);
std::regex_search('HELLO world', m, ci_re);   // matches

// Compile regex once (expensive constructor — never in a loop)
static const std::regex email_re(
    R'([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,})'
);
bool valid = std::regex_match('user@example.com', email_re);

Modules (C++20)

C++20 modules replace the textual #include model with a compiled binary interface. Each module is compiled once; importers consume the precompiled interface rather than re-parsing headers. This eliminates macro leakage, reduces build times, and makes the dependency graph explicit and tooling-friendly.

cpp
// Modules (C++20) replace #include with a cleaner dependency model.
// No header guard boilerplate, no macro leakage, faster build times.

// ---- math.cppm (module interface unit) ----
export module math;           // declare this file as the 'math' module

import <cmath>;               // import standard library header unit

export namespace math {

constexpr double PI = 3.14159265358979;

export double circle_area(double r) { return PI * r * r; }
export double hypotenuse(double a, double b) { return std::sqrt(a*a + b*b); }

export template<typename T>
T clamp(T val, T lo, T hi) {
    return val < lo ? lo : val > hi ? hi : val;
}

} // namespace math

// Internal linkage — not exported, invisible outside this module
static double impl_detail(double x) { return x * x; }

// ---- geometry.cppm (module interface unit) ----
export module geometry;
export import math;           // re-export math symbols to importers

export struct Point { double x, y; };
export double distance(Point a, Point b) {
    return math::hypotenuse(b.x - a.x, b.y - a.y);
}

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

int main() {
    std::cout << math::circle_area(5.0) << '\n';   // 78.5398...
    Point p1{0, 0}, p2{3, 4};
    std::cout << distance(p1, p2) << '\n';          // 5

    // Module partition (split large module across files):
    // export module math:algebra;   // partition file
    // import math:algebra;          // use partition within module
}

// Build with: g++ -std=c++20 -fmodules-ts math.cppm geometry.cppm main.cpp
// Or with CMake 3.28+: target_sources(app PUBLIC FILE_SET CXX_MODULES FILES math.cppm)

Sanitizers & Debugging

Compiler sanitizers instrument code at build time to detect bugs at runtime with precise diagnostics. AddressSanitizer finds memory errors (buffer overflows, use-after-free); UBSan catches undefined behaviour; ThreadSanitizer detects data races; MemorySanitizer finds uninitialized reads. Combine with -g -O1 for useful stack traces without excessive slowdown.

cpp
// Sanitizers: compiler-injected instrumentation that detects bugs at runtime.
// Enable with compiler flags — no source changes needed.

// ---- AddressSanitizer (ASan): detect memory errors ----
// g++ -fsanitize=address -fno-omit-frame-pointer -g

void asan_demo() {
    int arr[5]{};
    // arr[10] = 1;    // heap-buffer-overflow — ASan reports stack-buffer-overflow
    // int* p = new int(42); delete p; *p = 1;  // use-after-free
}

// ---- UndefinedBehaviorSanitizer (UBSan): detect UB ----
// g++ -fsanitize=undefined -g

void ubsan_demo() {
    int x = INT_MAX;
    // x = x + 1;     // signed integer overflow — UBSan fires
    int arr[3]{};
    // int y = arr[5];   // out-of-bounds — UBSan fires
    void* p = nullptr;
    // *static_cast<int*>(p) = 1;   // null dereference
}

// ---- ThreadSanitizer (TSan): detect data races ----
// g++ -fsanitize=thread -g

#include <thread>
int shared = 0;
void tsan_demo() {
    std::thread t1([]{ shared++; });   // race on 'shared'
    std::thread t2([]{ shared++; });
    t1.join(); t2.join();
    // TSan: data race on shared
}

// ---- MemorySanitizer (MSan): detect uninitialised reads ----
// clang++ -fsanitize=memory -g

void msan_demo() {
    int x;
    // if (x > 0) ...   // MSan: conditional jump on uninitialized value
}

// ---- Valgrind (external, no recompile needed) ----
// valgrind --leak-check=full ./app
// valgrind --tool=callgrind ./app   # profiling

// ---- static_assert: compile-time checks ----
struct Packet { uint8_t type; uint16_t len; uint8_t payload[256]; };
static_assert(sizeof(Packet) == 260, 'unexpected padding');
static_assert(alignof(double) == 8,  'unexpected alignment');

// ---- Debugging tips ----
// Compile with: -g -O0 -DDEBUG
// Use: assert(), [[nodiscard]], -Wall -Wextra -Wpedantic
// Avoid UB: -ftrapv (trap signed overflow), -fstack-protector-strong

Best Practices

Prefer Modern C++ Idioms

Use auto, range-for, structured bindings, emplace_back, std::array, nullptr, enum class, using aliases, override/final, and = delete/= default to write expressive, refactor-safe code.

cpp
// ── Prefer Modern C++ Idioms ──────────────────────────────────────────────

// auto: let the compiler deduce the type
auto v = std::vector<int>{1, 2, 3};   // ✓  not: std::vector<int> v = ...
auto it = v.begin();                  // ✓  iterator type is fragile to refactor

// range-for instead of index loop
for (const auto& x : v) { /* read */ }
for (auto& x : v)        { x *= 2;  }   // mutate

// structured bindings (C++17)
std::map<std::string, int> scores{{'Alice', 95}, {'Bob', 87}};
for (const auto& [name, score] : scores)
    std::cout << name << ':' << score << '\n';

// emplace_back constructs in-place — no temporary
v.emplace_back(42);               // ✓  not: v.push_back(42)  (trivial here)
std::vector<std::pair<int,int>> pairs;
pairs.emplace_back(1, 2);         // ✓  not: pairs.push_back({1, 2})

// std::array instead of C array — bounds, iterators, no decay
std::array<int, 5> arr{1, 2, 3, 4, 5};   // ✓  not: int arr[5] = {1,2,3,4,5}

// nullptr, not NULL or 0
int* p = nullptr;                 // ✓  not: int* p = NULL

// enum class — scoped, no implicit int conversion
enum class Color { Red, Green, Blue };
Color c = Color::Red;             // ✓  not: enum Color { Red, Green, Blue }

// using alias instead of typedef — supports templates
using StringVec = std::vector<std::string>;   // ✓  not: typedef std::vector<...>

// override / final — document and enforce overrides, catch typos
struct Base  { virtual void draw() const; };
struct Derived : Base { void draw() const override; };   // ✓

// = delete / = default — express intent explicitly
struct NonCopyable {
    NonCopyable(const NonCopyable&)            = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
    NonCopyable() = default;
};

Resource Ownership & RAII

Own every resource through a smart pointer or an RAII wrapper. Follow the rule of zero when possible, the rule of five only when managing a raw resource. Prefer make_unique/make_shared and stack allocation over naked new/delete.

cpp
// ── Resource Ownership & RAII ─────────────────────────────────────────────

// ✓ unique_ptr — sole, non-sharing ownership, zero overhead
auto conn = std::make_unique<Connection>(host, port);
// conn freed automatically when it leaves scope — no naked delete

// ✓ shared_ptr — shared ownership via ref-count
auto cfg = std::make_shared<Config>();
auto copy = cfg;   // ref-count 2; freed when last owner dies

// ✗ Avoid owning raw pointers
// Connection* conn = new Connection(host, port);  // who deletes?

// ✓ make_unique / make_shared — exception-safe, single allocation
auto w = std::make_unique<Widget>(42);
auto s = std::make_shared<Session>(token);

// ✗ Never naked new/delete in application code
// Widget* w = new Widget(42);   delete w;  // easy to leak

// ✓ Rule of Zero — if you use smart pointers/standard containers
//   the compiler-generated special members are correct
struct Config {
    std::string  name;
    std::vector<int> values;
    // no destructor, no copy/move needed — rule of zero
};

// ✓ Rule of Five — only when you manage a raw resource
class Buffer {
    std::unique_ptr<int[]> data_;
    std::size_t size_;
public:
    explicit Buffer(std::size_t n) : data_(std::make_unique<int[]>(n)), size_(n) {}
    // Copy, move, destructor all automatically correct via unique_ptr
};

// ✓ Prefer stack over heap for small, fixed-size objects
std::array<char, 256> local_buf{};   // stack
// not: auto buf = std::make_unique<char[]>(256);  // unnecessary heap

// ✓ RAII wrapper for C resources
class FilePtr {
    FILE* f_;
public:
    explicit FilePtr(const char* path, const char* mode) : f_(std::fopen(path, mode)) {}
    ~FilePtr() { if (f_) std::fclose(f_); }
    FILE* get() const { return f_; }
};

Template & Generic Code

Constrain templates with concepts (C++20) for readable error messages. Use if constexpr instead of SFINAE, provide meaningful static_assert messages, and use explicit template instantiation to control compile times.

cpp
// ── Template & Generic Code ───────────────────────────────────────────────

// ✓ Constrain with concepts (C++20) — clear errors, self-documenting
template<std::integral T>
T clamp(T v, T lo, T hi) { return v < lo ? lo : v > hi ? hi : v; }

// ✗ Unconstrained — confusing error 50 lines into instantiation
// template<typename T>
// T clamp(T v, T lo, T hi) { return v < lo ? lo : v > hi ? hi : v; }

// ✓ Custom concept
template<typename T>
concept Addable = requires(T a, T b) { { a + b } -> std::same_as<T>; };

template<Addable T>
T sum(T a, T b) { return a + b; }

// ✓ if constexpr — cleaner than SFINAE for compile-time branching
template<typename T>
std::string to_str(T val) {
    if constexpr (std::is_arithmetic_v<T>)
        return std::to_string(val);
    else
        return std::string(val);
}

// ✓ Meaningful static_assert messages
template<typename T>
void store(T val) {
    static_assert(std::is_trivially_copyable_v<T>,
        'store<T>: T must be trivially copyable for memcpy-based serialisation');
}

// ✓ Explicit template instantiation in .cpp — avoids re-instantiating in every TU
// In math.cpp:
//   template int clamp<int>(int, int, int);
// In math.h:
//   extern template int clamp<int>(int, int, int);

// ✓ Document template requirements in a comment when concepts are unavailable
// T must be LessThanComparable and DefaultConstructible
template<typename T>
T max_default(T a, T b) { return a < b ? b : a; }

Error Handling

Reserve exceptions for truly exceptional conditions. Return std::optional for nullable results, std::expected for recoverable failures. Mark non-throwing functions noexcept, never throw from destructors, and catch by const reference.

cpp
// ── Error Handling ────────────────────────────────────────────────────────

// ✓ Exceptions for truly exceptional, unrecoverable conditions
void open_db(const std::string& path) {
    if (!fs::exists(path))
        throw std::runtime_error('database not found: ' + path);
}

// ✓ std::optional for nullable / may-not-exist returns
std::optional<User> find_user(int id) {
    if (auto it = db.find(id); it != db.end()) return it->second;
    return std::nullopt;
}
if (auto u = find_user(42)) { /* use *u */ }

// ✓ std::expected for expected failures (C++23)
std::expected<int, std::string> parse(const std::string& s) {
    try { return std::stoi(s); }
    catch (...) { return std::unexpected('not a number: ' + s); }
}
auto r = parse('abc');
if (!r) std::cerr << r.error();

// ✓ noexcept — annotate functions that provably cannot throw
void swap_vals(int& a, int& b) noexcept { std::swap(a, b); }

// ✗ NEVER throw from a destructor — terminates the program during stack unwind
struct Bad {
    ~Bad() { throw std::runtime_error('boom'); }   // ✗ undefined behaviour
};
struct Good {
    ~Good() noexcept { /* clean up, log, but never throw */ }
};

// ✓ Catch by const reference — avoid slicing, avoid copy
try {
    open_db('/missing');
} catch (const std::exception& e) {   // ✓
    std::cerr << e.what() << '\n';
}
// ✗  } catch (std::exception e) {    // slices derived types

// ✓ Aim for the strong exception guarantee:
//   if an operation throws, the program state is unchanged
std::vector<int> committed;
void append_atomic(std::vector<int>& v, int x) {
    auto tmp = v;     // copy first
    tmp.push_back(x); // mutate copy — may throw
    v = std::move(tmp); // commit: move is noexcept
}

Performance

Pass large objects by const&, return by value (trust NRVO), prefer pre-increment, reserve containers when size is known, use emplace over insert, and prefer string_view for read-only string parameters. Profile before optimising.

cpp
// ── Performance ───────────────────────────────────────────────────────────

// ✓ Pass large objects by const ref — no copy
void process(const std::vector<double>& data) { /* ... */ }
// ✗  void process(std::vector<double> data)  — copies every element

// ✓ Return by value — NRVO / RVO eliminates the copy in practice
std::vector<int> build_range(int n) {
    std::vector<int> v;
    v.reserve(n);
    for (int i = 0; i < n; ++i) v.push_back(i);
    return v;   // NRVO: no copy, constructed directly in caller
}

// ✓ Pre-increment for non-trivial iterators
for (auto it = m.begin(); it != m.end(); ++it) { /* ... */ }  // ✓ ++it
// ✗  it++  — creates a temporary for map/list iterators

// ✓ std::move to avoid unnecessary copies
std::string build_greeting(std::string name) {
    name.insert(0, 'Hello, ');
    return std::move(name);   // avoid copy of local (or just return name — NRVO)
}

// ✓ reserve when size is known
std::vector<int> v;
v.reserve(1000);              // single allocation, no reallocations
for (int i = 0; i < 1000; ++i) v.push_back(i);

// ✓ emplace over insert — construct in-place
std::map<int, std::string> m;
m.emplace(1, 'one');          // ✓  not: m.insert({1, 'one'})

// ✓ string_view for read-only string parameters — accepts all string types, no alloc
void log_prefix(std::string_view prefix, std::string_view msg) {
    std::cout << prefix << ': ' << msg << '\n';
}

// ✓ Avoid virtual in hot paths — use CRTP or if constexpr instead
// ✗  Calling a virtual method in a tight loop prevents inlining and devirtualisation

// ✓ Profile before optimising
// Use: perf, Valgrind/Callgrind, gprof, or compiler flags -pg
// Premature micro-optimisation obscures intent; measure first

Safety & Correctness

Enable -Wall -Wextra -Wpedantic and sanitizers (ASan/UBSan) in CI. Use [[nodiscard]], mark getters const, prefer std::span over pointer+size pairs, use .at() for bounds-checked access, and never silently discard [[nodiscard]] results.

cpp
// ── Safety & Correctness ──────────────────────────────────────────────────

// ✓ Enable full warnings — treat them as errors in CI
// g++ -Wall -Wextra -Wpedantic -Werror -std=c++20

// ✓ Enable sanitizers in debug / CI builds
// g++ -fsanitize=address,undefined -fno-omit-frame-pointer -g
// g++ -fsanitize=thread -g

// ✓ [[nodiscard]] — force callers to check return values
[[nodiscard]] std::error_code write_file(const fs::path& p, std::string_view data);
[[nodiscard]] bool            try_lock();

// Calling code must use the result — compiler warns otherwise
auto ec = write_file('out.bin', payload);
if (ec) std::cerr << ec.message() << '\n';

// ✓ Mark getters const
struct Vec2 {
    double x, y;
    double length() const { return std::sqrt(x*x + y*y); }   // ✓ const
    // double length() { ... }   // ✗ cannot call on const Vec2
};

// ✓ std::span instead of raw pointer + size
void fill_buffer(std::span<int> buf, int val) {
    for (auto& x : buf) x = val;
}
std::array<int, 8> arr{};
fill_buffer(arr, 0);   // ✓ span wraps the array safely

// ✓ Avoid signed/unsigned comparison — use consistent types
std::vector<int> v{1, 2, 3};
for (std::size_t i = 0; i < v.size(); ++i) { /* ... */ }
// ✗  for (int i = 0; i < v.size(); ...)  — signed/unsigned mismatch warning

// ✓ Bounds-checked access in debug mode
int safe = v.at(2);    // throws std::out_of_range if out of bounds
// int unsafe = v[2];  // UB if out of bounds — only ok when index is provably valid

// ✓ Never ignore [[nodiscard]] results — use std::ignore only when truly intentional
std::ignore = try_lock();   // document the deliberate discard