thecodex.expert · The Codex Family of Knowledge
Snippets

C++ Snippets

13 idiomatic modern C++ patterns — copy, paste, adapt.

C++20/23 13 snippets Modern C++ only
C++smart_pointers.cpp
Smart pointers: unique_ptr and shared_ptr
#include <memory>
#include <iostream>

struct Widget {
    int id;
    Widget(int i) : id(i) { std::cout << "Widget " << id << " created\n"; }
    ~Widget() { std::cout << "Widget " << id << " destroyed\n"; }
    void use() { std::cout << "Using " << id << "\n"; }
};

int main() {
    // unique_ptr: sole ownership, deleted when out of scope
    auto w1 = std::make_unique<Widget>(1);
    w1->use();
    // auto w2 = w1;   // COMPILE ERROR: can't copy unique_ptr
    auto w2 = std::move(w1);  // transfer ownership — w1 is now nullptr

    // shared_ptr: shared ownership via atomic reference count
    auto s1 = std::make_shared<Widget>(2);
    {
        auto s2 = s1;  // both own Widget 2 — ref count = 2
        std::cout << "count: " << s1.use_count() << "\n";  // 2
    }  // s2 destroyed — ref count = 1
    std::cout << "count: " << s1.use_count() << "\n";  // 1

    // weak_ptr: observe without owning — breaks circular refs
    std::weak_ptr<Widget> weak = s1;
    if (auto locked = weak.lock()) {  // try to get shared_ptr
        locked->use();
    }
    s1.reset();  // Widget 2 destroyed here
    std::cout << "expired: " << weak.expired() << "\n";  // 1
}
C++raii.cpp
RAII: resource management via destructors
#include <fstream>
#include <mutex>
#include <iostream>
#include <stdexcept>

// RAII file handle: closes even if exception thrown
class FileWriter {
    std::ofstream file;
public:
    explicit FileWriter(const std::string& path) : file(path) {
        if (!file) throw std::runtime_error("Cannot open: " + path);
    }
    ~FileWriter() { file.close(); }  // always runs — exception-safe

    void write(const std::string& text) { file << text; }
};

// RAII mutex lock — same idea as std::lock_guard
class ScopedLock {
    std::mutex& m;
public:
    explicit ScopedLock(std::mutex& mutex) : m(mutex) { m.lock(); }
    ~ScopedLock() { m.unlock(); }
};

// Modern: use std::lock_guard (same pattern, from stdlib)
std::mutex mtx;
void thread_safe_op() {
    std::lock_guard<std::mutex> guard(mtx);  // locked
    // ... critical section
}   // guard destroyed — mutex unlocked automatically

// scoped_lock for multiple mutexes (deadlock-free)
std::mutex m1, m2;
void multi_lock() {
    std::scoped_lock lock(m1, m2);  // locks both atomically
}
C++stl_algorithms.cpp
STL algorithms: sort, find, transform, accumulate
#include <algorithm>
#include <numeric>
#include <vector>
#include <string>
#include <iostream>

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

    std::sort(v.begin(), v.end());   // {1,2,3,4,5,6,7,8,9}
    std::sort(v.begin(), v.end(), std::greater<int>{});  // descending

    // Binary search (requires sorted range)
    bool found = std::binary_search(v.begin(), v.end(), 5);

    // find_if: returns iterator to first match
    auto it = std::find_if(v.begin(), v.end(), [](int x){ return x > 5; });
    if (it != v.end()) std::cout << "First >5: " << *it << "\n";

    // count_if
    int odds = std::count_if(v.begin(), v.end(), [](int x){ return x % 2 != 0; });

    // transform: apply function to each element
    std::vector<int> doubled(v.size());
    std::transform(v.begin(), v.end(), doubled.begin(), [](int x){ return x * 2; });

    // accumulate: fold to single value
    int sum     = std::accumulate(v.begin(), v.end(), 0);
    int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>{});

    // partition: split in-place by predicate
    auto mid = std::partition(v.begin(), v.end(), [](int x){ return x % 2 == 0; });
    // elements before mid are even, after are odd

    // remove_if + erase (erase-remove idiom)
    v.erase(std::remove_if(v.begin(), v.end(), [](int x){ return x > 7; }), v.end());

    std::cout << "sum=" << sum << " product=" << product << "\n";
}
C++lambdas.cpp
Lambdas: captures, mutable, and generic
#include <functional>
#include <iostream>
#include <vector>

int main() {
    int base = 10;

    // [=] capture by value, [&] capture by reference
    auto add_base = [base](int x) { return x + base; };  // captures base by value
    auto modify   = [&base]() { base *= 2; };            // captures by reference

    std::cout << add_base(5) << "\n";  // 15
    modify();
    std::cout << base << "\n";  // 20

    // mutable: allow modifying value-captured variables
    int counter = 0;
    auto count = [counter]() mutable { return ++counter; };
    std::cout << count() << count() << "\n";  // 1 2
    std::cout << counter << "\n";  // 0 — original unchanged

    // Generic lambda (auto parameter) — C++14
    auto identity = [](auto x) { return x; };
    std::cout << identity(42) << " " << identity("hello") << "\n";

    // Lambda as callback stored in std::function
    std::function<int(int)> square = [](int x) { return x * x; };

    // Immediately invoked lambda expression (IILE)
    const auto result = [&base]() {
        // complex initialisation logic
        return base * base;
    }();  // called immediately

    // Capture by move (C++14)
    auto large_data = std::vector<int>(1000, 42);
    auto process = [data = std::move(large_data)]() {
        return data.size();  // data moved into lambda
    };
    std::cout << process() << "\n";  // 1000
}
C++optional_variant.cpp
std::optional and std::variant (C++17)
#include <optional>
#include <variant>
#include <string>
#include <iostream>

std::optional<int> safe_divide(int a, int b) {
    if (b == 0) return std::nullopt;
    return a / b;
}

int main() {
    auto result = safe_divide(10, 3);
    if (result) {
        std::cout << "result: " << *result << "\n";  // 3
    }
    std::cout << result.value_or(-1) << "\n";  // 3

    auto bad = safe_divide(5, 0);
    std::cout << bad.value_or(-1) << "\n";  // -1

    // variant: type-safe union — holds exactly one of its types
    using Value = std::variant<int, double, std::string>;

    Value v = 42;
    v = 3.14;
    v = std::string("hello");

    // Visit: pattern match all types
    std::visit([](auto&& arg) {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, int>)
            std::cout << "int: " << arg << "\n";
        else if constexpr (std::is_same_v<T, double>)
            std::cout << "double: " << arg << "\n";
        else
            std::cout << "string: " << arg << "\n";
    }, v);

    // std::get — throws std::bad_variant_access if wrong type
    if (std::holds_alternative<std::string>(v)) {
        std::cout << std::get<std::string>(v) << "\n";  // "hello"
    }
}
C++move_semantics.cpp
Move semantics and rule of five
#include <iostream>
#include <utility>
#include <vector>

class Buffer {
    int* data;
    size_t size;
public:
    explicit Buffer(size_t n) : data(new int[n]()), size(n) {
        std::cout << "construct " << n << "\n";
    }
    ~Buffer() { delete[] data; std::cout << "destroy\n"; }

    // Copy constructor: deep copy
    Buffer(const Buffer& o) : data(new int[o.size]), size(o.size) {
        std::copy(o.data, o.data + size, data);
        std::cout << "copy\n";
    }

    // Move constructor: steal resources — noexcept enables vector optimisation
    Buffer(Buffer&& o) noexcept : data(o.data), size(o.size) {
        o.data = nullptr; o.size = 0;  // leave source in valid empty state
        std::cout << "move\n";
    }

    // Copy and move assignment omitted for brevity — follow same pattern
    Buffer& operator=(const Buffer&) = delete;
    Buffer& operator=(Buffer&&) = delete;

    size_t getSize() const { return size; }
};

int main() {
    Buffer b1(100);
    Buffer b2 = std::move(b1);   // "move" printed — O(1), not O(n)
    std::cout << "b2 size: " << b2.getSize() << "\n";  // 100
    std::cout << "b1 size: " << b1.getSize() << "\n";  // 0

    // emplace_back: constructs in-place, avoids copy/move
    std::vector<Buffer> buffers;
    buffers.reserve(3);
    buffers.emplace_back(50);  // constructs directly — no move needed
    buffers.emplace_back(100);
}
C++ranges.cpp
C++20 ranges and views
#include <ranges>
#include <vector>
#include <iostream>
#include <string>

int main() {
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // Range pipeline: lazy, composable — no intermediate containers
    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 x : result) std::cout << x << " ";  // 4 16 36
    std::cout << "\n";

    // std::ranges::sort: cleaner than sort(v.begin(), v.end())
    std::ranges::sort(v);

    // iota: generate sequence lazily
    for (int i : std::views::iota(1, 6)) std::cout << i << " ";  // 1 2 3 4 5

    // zip (C++23)
    // std::vector names{"Alice","Bob","Carol"};
    // std::vector scores{95,87,92};
    // for (auto [name,score] : std::views::zip(names, scores))
    //     std::cout << name << ": " << score << "\n";

    // Reverse view
    for (int x : v | std::views::reverse | std::views::take(3)) {
        std::cout << x << " ";  // 10 9 8
    }

    // split view: tokenise
    std::string csv = "one,two,three,four";
    for (auto part : csv | std::views::split(',')) {
        std::cout << std::string(part.begin(), part.end()) << "\n";
    }
}
C++structured_bindings.cpp
Structured bindings and if constexpr (C++17)
#include <map>
#include <tuple>
#include <string>
#include <iostream>
#include <type_traits>

struct Point { double x, y, z; };

int main() {
    // Structured bindings: unpack pairs, tuples, and structs
    auto [a, b] = std::make_pair(1, "hello");
    auto [x, y, z] = Point{1.0, 2.0, 3.0};

    std::map<std::string, int> scores{{"Alice", 95}, {"Bob", 87}};
    for (const auto& [name, score] : scores) {
        std::cout << name << ": " << score << "\n";
    }

    // Map insert returns {iterator, bool} — use structured binding
    auto [it, inserted] = scores.insert({"Carol", 92});
    std::cout << "inserted: " << inserted << "\n";

    // if constexpr: compile-time branching in templates
    auto describe = []<typename T>(T val) {
        if constexpr (std::is_integral_v<T>)
            return std::string("integer: ") + std::to_string(val);
        else if constexpr (std::is_floating_point_v<T>)
            return std::string("float: ") + std::to_string(val);
        else
            return std::string("other");
    };

    std::cout << describe(42) << "\n";     // integer: 42
    std::cout << describe(3.14) << "\n";   // float: 3.140000
}
C++concepts.cpp
C++20 concepts
#include <concepts>
#include <iostream>

// Named concept: constraint on template parameter
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;

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

// Use concept as constraint
template<Numeric T>
T square(T x) { return x * x; }

// requires clause: inline constraint
template<typename T>
requires std::totally_ordered<T>
T clamp(T val, T lo, T hi) {
    return (val < lo) ? lo : (val > hi) ? hi : val;
}

// Abbreviated template syntax (C++20)
auto add(Numeric auto a, Numeric auto b) { return a + b; }

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

template<Container C>
void print_container(const C& c) {
    for (const auto& elem : c) std::cout << elem << " ";
    std::cout << "\n";
}

int main() {
    std::cout << square(5) << "\n";      // 25
    std::cout << square(3.14) << "\n";   // 9.8596
    // square("hello");  // clear error: constraint not satisfied
    std::cout << clamp(15, 0, 10) << "\n";  // 10
}
C++templates.cpp
Function and class templates
#include <iostream>
#include <vector>
#include <stdexcept>

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

// Class template
template<typename T>
class Stack {
    std::vector<T> items;
public:
    void push(T item) { items.push_back(std::move(item)); }

    T pop() {
        if (empty()) throw std::underflow_error("stack is empty");
        T top = std::move(items.back());
        items.pop_back();
        return top;
    }

    const T& peek() const {
        if (empty()) throw std::underflow_error("stack is empty");
        return items.back();
    }

    bool empty() const { return items.empty(); }
    size_t size() const { return items.size(); }
};

// Template specialisation for const char* — special comparison
template<>
class Stack<const char*> {
    // Different implementation for C-strings
};

// Variadic templates (C++11): any number of arguments
template<typename... Args>
void print_all(Args&&... args) {
    ((std::cout << args << " "), ...);  // fold expression
    std::cout << "\n";
}

int main() {
    Stack<int> s;
    s.push(1); s.push(2); s.push(3);
    std::cout << s.pop() << "\n";  // 3
    std::cout << s.peek() << "\n"; // 2

    print_all(1, 2.0, "hello", true);  // 1 2 hello 1
}
C++string_format.cpp
std::string and std::format (C++20)
#include <string>
#include <sstream>
#include <format>  // C++20
#include <iostream>
#include <algorithm>

int main() {
    std::string s = "Hello, World!";

    // Substring, find, replace
    std::cout << s.substr(7, 5) << "\n";  // World
    auto pos = s.find("World");
    if (pos != std::string::npos) s.replace(pos, 5, "C++");
    std::cout << s << "\n";  // Hello, C++!

    // Case conversion
    std::string upper = s;
    std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);

    // String building with ostringstream
    std::ostringstream oss;
    for (int i = 0; i < 5; ++i) oss << i << (i < 4 ? "," : "");
    std::string csv = oss.str();  // "0,1,2,3,4"

    // std::format (C++20): type-safe printf
    auto msg = std::format("Pi ≈ {:.4f}, count = {:05d}", 3.14159, 42);
    std::cout << msg << "\n";  // "Pi ≈ 3.1416, count = 00042"

    // Named arguments (C++23)
    // auto s2 = std::format("{name} is {age}", std::arg("name","Alice"), std::arg("age",30));

    // string_view: non-owning reference — avoids copies
    std::string_view sv = s;
    std::cout << sv.substr(0, 5) << "\n";  // Hello
}
C++concurrency.cpp
std::thread, atomic, and futures
#include <thread>
#include <atomic>
#include <future>
#include <vector>
#include <iostream>

int main() {
    // std::atomic: thread-safe operations without mutex
    std::atomic<int> counter{0};

    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back([&counter]() {
            counter.fetch_add(1, std::memory_order_relaxed);
        });
    }
    for (auto& t : threads) t.join();
    std::cout << "counter: " << counter << "\n";  // 10

    // std::async: run function asynchronously
    auto future = std::async(std::launch::async, []() {
        // runs in a thread pool thread
        return 42;
    });
    std::cout << "result: " << future.get() << "\n";  // 42 (blocks if not ready)

    // std::promise / std::future: manual control
    std::promise<std::string> promise;
    std::future<std::string> fut = promise.get_future();

    std::thread producer([&promise]() {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        promise.set_value("result from thread");
    });

    std::cout << fut.get() << "\n";  // blocks until producer sets value
    producer.join();
}
C++containers.cpp
STL containers: vector, map, unordered_map
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <iostream>

int main() {
    // vector: prefer for most sequences
    std::vector<int> v{3, 1, 4, 1, 5, 9};
    v.reserve(20);           // pre-allocate to avoid reallocations
    v.emplace_back(2);       // construct in-place (no copy/move overhead)
    v.erase(v.begin() + 1);  // erase at index 1

    // map: balanced BST — O(log n), sorted by key
    std::map<std::string, int> m;
    m["alice"] = 95;
    m.emplace("bob", 87);         // emplace: construct in-place
    m.try_emplace("alice", 100);  // does NOT overwrite if key exists
    std::cout << m["alice"] << "\n";  // 95

    // unordered_map: hash table — O(1) average
    std::unordered_map<std::string, int> um;
    um.reserve(100);  // set expected size to avoid rehashing

    // Safe lookup — [] inserts default if missing
    auto it = um.find("missing");
    if (it != um.end()) { /* found */ }
    // um["missing"] would create it with value 0

    // Frequency count
    std::vector<std::string> words{"go","go","rust","cpp","go"};
    std::unordered_map<std::string, int> freq;
    for (const auto& w : words) freq[w]++;
    std::cout << freq["go"] << "\n";  // 3

    // set: unique sorted elements
    std::set<int> s{3,1,4,1,5,9,2,6};
    std::cout << s.size() << "\n";  // 7 (duplicate 1 removed)
}
C++ referenceC++ overview · Learn C++
← Rust Kotlin snippets →
Everything C++ in one place — learning paths, reference, playground, and more. C++ Hub →