Skip to content

Latest commit

 

History

History
521 lines (359 loc) · 12.8 KB

File metadata and controls

521 lines (359 loc) · 12.8 KB

C++ STL, Concurrency, and Modern Features - Comprehensive Reference

All information verified against cppreference.com (the authoritative C++ reference).


1. Containers

1.1 std::vector (header: )


1.2 std::map (header: )

1.3 std::unordered_map (header: <unordered_map>)

1.4 std::deque (header: )

1.5 std::list (header: )

1.6 std::set and std::unordered_set

1.7 std::array (header: , C++11)

2. Iterators and Ranges (C++20)

2.1 Iterator Categories (Hierarchy from weakest to strongest)

2.2 Iterator Operations

2.3 Range-based for Loop

2.4 std::ranges (C++20)

2.5 Custom Iterator

3. Algorithms (header: )

3.1 Sorting

3.2 Non-modifying Algorithms

#include <algorithm>
#include <numeric>
#include <vector>
#include <iostream>

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

    // for_each
    std::for_each(v.begin(), v.end(), [](int& x) { x *= 2; });
    // v = {2, 4, 6, 8, 10}

    // find
    auto it = std::find(v.begin(), v.end(), 6);
    if (it != v.end()) std::cout << "Found: " << *it << "\n";  // 6

    // count
    int n = std::count(v.begin(), v.end(), 4);
    std::cout << "Count: " << n << "\n";  // 1

    // accumulate (header: <numeric>)
    int sum = std::accumulate(v.begin(), v.end(), 0);
    std::cout << "Sum: " << sum << "\n";  // 30
}

3.3 Modifying Algorithms

#include <algorithm>
#include <vector>
#include <iostream>

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

    // transform
    std::vector<int> result(v.size());
    std::transform(v.begin(), v.end(), result.begin(),
                   [](int x) { return x * x; });
    // result = {1, 4, 9, 16, 25}

    // copy
    std::vector<int> dest(3);
    std::copy(v.begin(), v.begin() + 3, dest.begin());

    // move (algorithm, not std::move the utility)
    std::vector<std::string> src = {"hello", "world"};
    std::vector<std::string> dst(src.size());
    std::move(src.begin(), src.end(), dst.begin());

    // remove_if (erase-remove idiom)
    std::vector<int> v2 = {1, 2, 3, 2, 5, 2};
    v2.erase(std::remove_if(v2.begin(), v2.end(),
             [](int x) { return x == 2; }), v2.end());
    // v2 = {1, 3, 5}
}

4. String and Utilities

4.1 std::string (header: )

4.2 std::string_view (header: <string_view>, C++17)

4.3 std::span (header: , C++20)


5. Error Handling

5.1 std::optional (header: , C++17)

5.2 std::expected (header: , C++23)

5.3 std::variant (header: , C++17)


6. Concurrency

6.1 std::thread (header: , C++11)

6.2 std::jthread (header: , C++20)

6.3 Mutexes and Locks

#include <mutex>
#include <thread>
#include <vector>
#include <iostream>

std::mutex mtx;
int counter = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);  // RAII lock, unlocks on scope exit
    ++counter;
}

// std::scoped_lock (C++17): can lock multiple mutexes at once (deadlock-free)
std::mutex m1, m2;
void transfer() {
    std::scoped_lock lock(m1, m2);  // locks both atomically
}

int main() {
    std::vector<std::jthread> threads;
    for (int i = 0; i < 10; ++i)
        threads.emplace_back(increment);

    std::cout << "Counter: " << counter << "\n";  // 10 (always correct)
}

6.4 std::condition_variable (header: <condition_variable>)

6.5 std::promise / std::future / std::async (header: )

6.6 std::atomic and Memory Ordering

#include <atomic>
#include <thread>
#include <iostream>

std::atomic<int> counter{0};

void increment() {
    for (int i = 0; i < 10000; ++i)
        counter.fetch_add(1, std::memory_order_relaxed);
}

int main() {
    std::thread t1(increment), t2(increment);
    t1.join(); t2.join();
    std::cout << "Counter: " << counter << "\n";  // 20000 (always correct)
}

Memory Orderings (from ):

Ordering Description
memory_order_relaxed Atomicity only; no ordering constraints. Good for counters.
memory_order_acquire No reads/writes in current thread can be reordered before this load.
memory_order_release No reads/writes in current thread can be reordered after this store.
memory_order_acq_rel Both acquire (on load) and release (on store). For RMW operations.
memory_order_seq_cst Default. Single total order across all threads. Strongest guarantee.
memory_order_consume Deprecated (C++26). Like acquire but only for data-dependent operations.

Release-Acquire Pattern:

#include <atomic>
#include <thread>
#include <string>
#include <cassert>

std::atomic<std::string*> ptr;
int data;

void producer() {
    std::string* p = new std::string("Hello");
    data = 42;
    ptr.store(p, std::memory_order_release);  // all prior writes visible
}

void consumer() {
    std::string* p2;
    while (!(p2 = ptr.load(std::memory_order_acquire)))
        ;
    assert(*p2 == "Hello");  // guaranteed
    assert(data == 42);      // guaranteed (released before the store)
}

int main() {
    std::thread t1(producer), t2(consumer);
    t1.join(); t2.join();
}

Data Race / Happens-Before:

  • A data race occurs when two threads access the same memory location, at least one writes, and there is no happens-before relationship.
  • happens-before is the fundamental ordering guarantee: if A happens-before B, then A's effects are visible to B.
  • Atomic operations with sufficient ordering (acquire/release, seq_cst) establish happens-before relationships.
  • Without synchronization, the compiler and CPU may reorder operations freely.

6.7 Additional Concurrency Primitives

std::atomic_flag (header: ): Lock-free boolean atomic; the only type guaranteed lock-free on all implementations.

#include <atomic>
#include <thread>
#include <iostream>

std::atomic_flag flag = ATOMIC_FLAG_INIT;

void spinlock_acquire() {
    while (flag.test_and_set(std::memory_order_acquire))  // spin
        ;
}

void spinlock_release() {
    flag.clear(std::memory_order_release);
}

int main() {
    std::thread t1([]{
        spinlock_acquire();
        std::cout << "Thread 1 in critical section\n";
        spinlock_release();
    });
    std::thread t2([]{
        spinlock_acquire();
        std::cout << "Thread 2 in critical section\n";
        spinlock_release();
    });
    t1.join(); t2.join();
}

7. File I/O (header: )


8. Random (header: )


9. std::chrono (header: )


10. std::function and std::bind (header: )

std::function

A general-purpose polymorphic function wrapper. Can store functions, lambdas, bind expressions, function objects, and member function pointers.

#include <functional>
#include <iostream>

int add(int a, int b) { return a + b; }

struct Multiplier {
    int factor;
    int operator()(int x) const { return x * factor; }
};

int main() {
    // Store a free function
    std::function<int(int, int)> f = add;
    std::cout << f(3, 4) << "\n";  // 7

    // Store a lambda
    std::function<int(int)> square = [](int x) { return x * x; };
    std::cout << square(5) << "\n";  // 25

    // Store a function object
    std::function<int(int)> mul = Multiplier{10};
    std::cout << mul(5) << "\n";  // 50

    // Check if empty
    std::function<void()> empty;
    if (!empty) std::cout << "Empty function\n";
    // Calling empty throws std::bad_function_call

    // Capture variables in lambda
    int offset = 100;
    std::function<int(int)> add_offset = [offset](int x) { return x + offset; };
}

std::bind (and modern alternatives)

#include <functional>
#include <iostream>

void greet(std::string greeting, std::string name) {
    std::cout << greeting << ", " << name << "!\n";
}

int main() {
    // std::bind
    using namespace std::placeholders;
    auto hello = std::bind(greet, "Hello", _1);
    hello("World");  // Hello, World!

    // Modern alternative: lambda (generally preferred)
    auto goodbye = [](std::string name) { greet("Goodbye", name); };
    goodbye("World");

    // C++20: std::bind_front (preferred over bind for partial application)
    auto hi = std::bind_front(greet, "Hi");
    hi("Everyone");  // Hi, Everyone!
}

11. Bit Manipulation

Bitwise Operators

std::bitset (header: )

C++20 Bit Functions (header: )

#include <bit>
#include <bitset>
#include <cstdint>
#include <iostream>

int main() {
    uint32_t x = 0b00000000000000000000000101100000u;

    // Population count (number of set bits)
    std::cout << "popcount: " << std::popcount(x) << "\n";      // 3

    // Count leading zeros (from MSB)
    std::cout << "countl_zero: " << std::countl_zero(x) << "\n"; // 25

    // Count leading ones
    // Count trailing zeros (from LSB)
    std::cout << "countr_zero: " << std::countr_zero(x) << "\n"; // 5

    // Count trailing ones
    // Bit width (number of bits needed to represent x)
    std::cout << "bit_width: " << std::bit_width(x) << "\n";     // 7

    // Powers of 2
    std::cout << "has_single_bit: " << std::has_single_bit(8u) << "\n";  // true (power of 2)
    std::cout << "bit_ceil(5): " << std::bit_ceil(5u) << "\n";   // 8
    std::cout << "bit_floor(5): " << std::bit_floor(5u) << "\n"; // 4

    // Rotate
    uint8_t val = 0b11000011;
    std::cout << "rotl: " << std::bitset<8>(std::rotl(val, 2)) << "\n";  // 00001111
    std::cout << "rotr: " << std::bitset<8>(std::rotr(val, 2)) << "\n";  // 11110000

    // Endianness
    if constexpr (std::endian::native == std::endian::little)
        std::cout << "Little endian\n";
}

12. std::format (header: , C++20)

A type-safe, performant replacement for printf and iostreams. Compile-time format string checking.

#include <format>
#include <iostream>
#include <string>

int main() {
    // Basic formatting
    std::cout << std::format("Hello {}!\n", "world");

    // Positional arguments
    std::cout << std::format("{1} {0}\n", "World", "Hello");  // "Hello World"

    // Number formatting
    std::cout << std::format("Int: {:d}\n", 42);             // Int: 42
    std::cout << std::format("Hex: {:x}\n", 255);            // Hex: ff
    std::cout << std::format("Hex: {:X}\n", 255);            // Hex: FF
    std::cout << std::format("Oct: {:o}\n", 255);            // Oct: 377
    std::cout << std::format("Bin: {:b}\n", 255);            // Bin: 11111111

    // Float formatting
    std::cout << std::format("Pi: {:.4f}\n", 3.14159);      // Pi: 3.1416
    std::cout << std::format("Sci: {:.2e}\n", 1234.5);       // Sci: 1.23e+03

    // Width and alignment
    std::cout << std::format("|{:>10}|\n", "right");         // |     right|
    std::cout << std::format("|{:<10}|\n", "left");          // |left      |
    std::cout << std::format("|{:^10}|\n", "center");        // |  center  |
    std::cout << std::format("|{:*^10}|\n", "center");       // |**center**|

    // Fill with zeros
    std::cout << std::format("Zero-padded: {:05d}\n", 42);   // 00042

    // String formatting
    std::string name = "C++";
    int version = 20;
    std::cout << std::format("{} {}\n", name, version);       // C++ 20

    // Dynamic format string (runtime)
    std::string fmt_str = "Value: {}\n";
    std::cout << std::vformat(fmt_str, std::make_format_args(42));
}

Format spec overview: {[arg-id]:[fill][align][sign][#][0][width][.precision][L][type]}

Specifier Meaning
d Decimal integer
x / X Hexadecimal
o Octal
b Binary
e / E Scientific float
f / F Fixed-point float
g / G General float
s String
p Pointer

Sources

All technical details verified against the following cppreference.com pages: