DevTools

Cheatsheet C++

Linguagem de alto desempenho com controlo de memória

Back to languages
C++
100 cards found
Categories:
Versions:

Basic Syntax


10 cards
Variables and types
int age = 30;
double price = 9.99;
char letter = 'A';
bool active = true;
std::string name = "Anna";
auto x = 42;          // type inferido (int)
unsigned int counter = 0;
long long big = 9999999999LL;

int, double, char, bool and std::string are the most used types. auto lets the compiler infer the type. unsigned only accepts positive values.

Input and output (I/O)
#include <iostream>

int main() {
    std::cout << "Name: ";
    std::string name;
    std::getline(std::cin, name);  // line completa

    int age;
    std::cout << "Age: ";
    std::cin >> age;

    std::cout << name << " tem " << age << " years\n";
    return 0;
}

std::cout prints, std::cin reads. << chains outputs. std::getline reads a whole line (with spaces). std::endl flushes — prefer "\n" for performance.

using and typedef
// Alias de type (moderno):
using Vec = std::vector<int>;
using Matrix = std::vector<std::vector<double>>;
using Callback = std::function<void(int)>;

Vec v = {1, 2, 3};

// typedef (legado):
typedef unsigned long ulong;

// Template alias:
template <typename T>
using Ptr = std::shared_ptr<T>;

Ptr<int> p = std::make_shared<int>(42);

using creates type aliases (more readable than typedef). It supports templates. Useful for simplifying long types like std::vector<std::vector<double>>. Standard in modern C++.

Fixed-size types
#include <cstdint>

int8_t   a = -128;      // 1 byte
uint8_t  b = 255;       // 1 byte without signal
int16_t  c = -32768;    // 2 bytes
int32_t  d = 2000000;   // 4 bytes
int64_t  e = 9e18;      // 8 bytes
uint64_t f = 18e18;     // 8 bytes without signal
size_t   n = 0;         // size (unsigned)

Types from <cstdint> guarantee an exact size on any platform. int32_t = 4 bytes always. size_t is used for sizes and indexes. Prefer these when the size matters.

Strings (std::string)
#include <string>

std::string s = "Hello World";
s.length();          // 10
s.substr(4, 5);     // "World"
s.find("World");    // 4 (position)
s.replace(0, 3, "Hi"); // "Hi World"
s += "!!";           // concatenation
s.empty();           // false
s.compare("Hi World!!"); // 0 if equal

// Formatting (C++20):
std::string r = std::format("{} has {}", name, age);

std::string is dynamic and safe. substr extracts, find searches (returns npos if not found). std::format (C++20) formats like Python. Avoid C-strings (char*).

Structure of a program
#include <iostream>
#include <vector>
#include <string>

// Forward declarations:
void process(const std::string& s);

int main() {
    std::vector<std::string> data = {"a", "b"};

    for (const auto& d : data) {
        process(d);
    }

    return 0;  // success
}

void process(const std::string& s) {
    std::cout << s << "\n";
}

Every C++ program has main() the the entry point. #include imports headers. return 0 indicates success. Functions can be declared before and defined later. Compile with g++ main.cpp -o app.

Constants
const int MAX = 100;          // immutable em runtime
constexpr double PI = 3.14159; // avaliada em compilation
constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

inline constexpr int TAM = 1024;

// enum class (tipado, without pollution):
enum class Color { Red, Green, Blue };
Color c = Color::Green;

const prevents modification. constexpr is evaluated at compile time (faster). enum class is typed and does not pollute the namespace (use Color::Green instead of just Green).

Type conversions
// Numeric → string:
std::string s = std::to_string(42);
std::string d = std::to_string(3.14);

// String → numeric:
int n = std::stoi("123");
double x = std::stod("3.14");
long l = std::stol("999999");

// Cast explicit:
int i = static_cast<int>(3.99);  // 3 (truncates)
double dd = static_cast<double>(7) / 2; // 3.5

// dynamic_cast (polymorphism):
Base* b = dynamic_cast<Derived*>(ptr);

std::to_string converts number→string. std::stoi/std::stod do the reverse. static_cast is the safe compile-time cast. dynamic_cast checks at runtime (inheritance).

Operators
// Arithmetic:
int r = 10 + 3;  // 13
int q = 10 / 3;  // 3 (integer!)
int m = 10 % 3;  // 1 (remainder)

// Comparison:
bool b = (5 == 5);  // true
bool c = (5 != 3);  // true

// Logical:
bool d = (true && false);  // false
bool e = (true || false);  // true

// Bitwise:
int f = 0b1010 & 0b1100;  // 0b1000
int g = 0b1010 | 0b1100;  // 1110

Arithmetic, comparison, logical and bitwise operators. Careful: 10 / 3 = 3 (integer division). % gives the remainder. && and || are short-circuit.

Arrays
// Array fixo (C-style):
int arr[5] = {1, 2, 3, 4, 5};
arr[0];  // 1

// std::array (prefer, C++11):
#include <array>
std::array<int, 5> a = {10, 20, 30, 40, 50};
a.size();   // 5
a.front();  // 10
a.back();   // 50

// Percorrer:
for (int x : a) std::cout << x << " ";

std::array is fixed but safe (it knows its size). C arrays (int arr[5]) do not know their own size. For a dynamic size, use std::vector. std::array does not allocate on the heap.

Flow Control


10 cards
if / else if / else
if (age >= 18) {
    std::cout << "adult\n";
} else if (age >= 13) {
    std::cout << "teenager\n";
} else {
    std::cout << "child\n";
}

// Sem keys (only 1 statement):
if (x > 0) std::cout << "positive";

if evaluates a boolean condition. else if chains checks. Braces are optional for 1 statement but always recommended. C++ has no elif — use else if.

while and do-while
// while: checks BEFORE executing
int n = 5;
while (n > 0) {
    std::cout << n-- << " ";
}

// do-while: runs AT LEAST once
int option;
do {
    std::cout << "Menu: ";
    std::cin >> option;
} while (option != 0);

while repeats while the condition is true. do-while guarantees at least 1 execution (checks at the end). Ideal for menus and input with validation.

Nested loops
// Matrix 3x3:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        std::cout << matrix[i][j] << " ";
    }
    std::cout << "\n";
}

// Com range-for:
for (const auto& line : matrix) {
    for (int val : line) {
        std::cout << val << " ";
    }
    std::cout << "\n";
}

Nested loops traverse 2D structures (matrices, grids). The outer loop controls rows, the inner one columns. A nested range-for is more readable. Be careful with performance in O(n²).

switch / case
switch (day) {
    case 1:
        std::cout << "Monday";
        break;
    case 2:
    case 3:
        std::cout << "Ter/Qua";
        break;
    default:
        std::cout << "Other";
        break;
}

switch compares against integer constants. break avoids fall-through. Cases can share code (2 and 3). default is the fallback. Faster than if-else for many cases (jump table).

break and continue
for (int i = 0; i < 100; i++) {
    if (i == 50) break;      // sai do ciclo
    if (i % 2 == 0) continue; // skips iteration
    std::cout << i << " ";   // only odd up to 49
}

// break em switch:
switch (x) {
    case 1: doWork(); break;  // without break = fall-through
}

break ends the loop immediately. continue jumps to the next iteration. In a switch, break avoids executing the next case. Use it sparingly for readability.

goto and labels
// Use legitimate: exit de loops nested
for (int i = 0; i < N; i++) {
    for (int j = 0; j < M; j++) {
        if (found(i, j)) {
            goto end;
        }
    }
}
end:
std::cout << "Search finished\n";

// AVOID: makes code unreadable
// Prefer: break com flag, or func com return

goto jumps to a label. The only acceptable use: exiting deeply nested loops. In general, avoid it — it makes the flow hard to follow. Prefer return, break or refactoring into a function.

Classic for
for (int i = 0; i < 10; i++) {
    std::cout << i << " ";
}

// Multiple variables:
for (int i = 0, j = 10; i < j; i++, j--) {
    std::cout << i << "," << j << " ";
}

// Decrescente:
for (int i = 9; i >= 0; i--) {
    std::cout << i << " ";
}

for has 3 parts: initialization, condition, increment. i++ is post-increment. It accepts multiple comma-separated variables. Ideal when you know the number of iterations.

Ternary operator
// condition ? value_if_true : value_if_false
int largest = (a > b) ? a : b;

std::string state = active ? "online" : "offline";

// Chained (avoid, barely readable):
char grade = (p >= 90) ? 'A'
          : (p >= 80) ? 'B'
          : (p >= 70) ? 'C' : 'F';

// With cout:
std::cout << (even ? "even" : "odd");

The ?: operator is an if-else in an expression. It returns a value. Ideal for simple assignments. Avoid chaining (unreadable). The parentheses in (cond) are optional but help.

Range-based for (C++11)
std::vector<int> nums = {10, 20, 30};

// Por value (copy):
for (int x : nums) std::cout << x;

// Por reference (modifica):
for (int& x : nums) x *= 2;

// Por const reference (eficiente, without copy):
for (const auto& item : nums) {
    std::cout << item << "\n";
}

for (auto& x : container) iterates without an index. const auto& avoids copies (essential for strings/objects). auto& allows modifying. More readable than iterators. Prefer it whenever possible.

if with initialization (C++17)
// Variable com escopo limitado ao if:
if (auto it = map.find("key"); it != map.end()) {
    std::cout << it->second;
}
// 'it' not exists here

if (int n = std::stoi(input); n > 0) {
    process(n);
}

// Also em switch:
switch (auto [ok, val] = try(); ok) {
    case true:  usar(val); break;
    case false: error();    break;
}

if (init; condition) declares a variable scoped to the block. It avoids polluting the outer scope. Very used with find() and conversions. It also works in switch. C++17 feature.

Functions


10 cards
Function with return
int add(int a, int b) {
    return a + b;
}

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

int main() {
    int r = add(3, 7);      // 10
    greeting("Anna");           // "Hello, Anna!"
}

Functions have a return type, a name and parameters. void = no return. return returns the value and ends. Parameters are passed by value (copy) by default.

Lambda expressions
// Lambda simple:
auto double = [](int x) { return x * 2; };
double(5);  // 10

// With capture by reference:
int total = 0;
auto accumulate = [&total](int x) { total += x; };
accumulate(10);
accumulate(20);  // total = 30

// As comparator:
std::sort(v.begin(), v.end(),
    [](int a, int b) { return a > b; });

// Capture by value:
auto copy = [x]() { return x; };

Lambda = inline anonymous function. [] is the capture: [&] by reference, [=] by value, [x] a specific variable. Ideal for callbacks and comparators in STL somethingrithms.

Functions with auto return
// Return inferido (C++14):
auto divide(double a, double b) {
    return a / b;  // returns double
}

// Trailing return type:
auto add(int a, int b) -> int {
    return a + b;
}

// Useful com types complexos:
auto getMap() {
    return std::map<std::string, std::vector<int>>{};
}

// decltype(auto) preserva reference:
decltype(auto) first(std::vector<int>& v) {
    return v[0];  // returns int&
}

auto the a return lets the compiler infer. -> type (trailing) is explicit. decltype(auto) preserves references and cv-qualifiers. Useful for long types and templates.

Passing by reference
// Modifica o original:
void increment(int& x) {
    x++;
}

// Only reading (without copy):
void print(const std::string& s) {
    std::cout << s << "\n";
    // s = "error";  // ERROR: is const
}

int n = 5;
increment(n);  // n = 6

& passes by reference (no copy, modifies the original). const & is read-only but avoids a copy — always use it for strings, vectors and objects. Essential for performance.

inline and constexpr
// inline: hint to avoid overhead de call
inline int abs(int x) {
    return x < 0 ? -x : x;
}

// constexpr: evaluated at compile time
constexpr int square(int n) {
    return n * n;
}

constexpr int ARR[5] = {
    square(1), square(2), square(3),
    square(4), square(5)
};
// ARR = {1, 4, 9, 16, 25} — everything em compilation

inline suggests to the compiler to expand the function (avoids call overhead). constexpr guarantees compile-time evaluation if the args are constant. Arrays and templates benefit from constexpr.

Variadic arguments (C-style)
#include <cstdarg>

double average(int n, ...) {
    va_list args;
    va_start(args, n);
    double sum = 0;
    for (int i = 0; i < n; i++) {
        sum += va_arg(args, double);
    }
    va_end(args);
    return sum / n;
}

average(3, 10.0, 20.0, 30.0);  // 20.0

va_list/va_arg allow a variable number of arguments (C style). Unsafe (no type-check). In modern C++, prefer variadic templates or std::initializer_list.

Default parameters
void connect(const std::string& host = "localhost",
              int port = 3306,
              bool ssl = false) {
    std::cout << host << ":" << port
              << (ssl ? " (SSL)" : "") << "\n";
}

connect();                    // localhost:3306
connect("db.local");          // db.local:3306
connect("db.local", 5432);    // db.local:5432
connect("db.local", 5432, true);

Parameters with = value are optional in the call. They must be at the end of the list. Calls can omit the last arguments. It avoids unnecessary overloads.

noexcept and specifications
// Promises not throw exceptions:
void swap(int& a, int& b) noexcept {
    int tmp = a;
    a = b;
    b = tmp;
}

// Condicional:
template <typename T>
void move(T& a, T& b) noexcept(noexcept(T(std::move(a)))) {
    T tmp = std::move(a);
    a = std::move(b);
    b = std::move(tmp);
}

// Check:
static_assert(noexcept(swap(a, b)));

noexcept promises that the function does not throw exceptions. It allows optimizations (move instead of copy in vector). If violated, it calls std::terminate. Essential in move constructors and swap.

Function overloading
int sum(int a, int b) { return a + b; }
double sum(double a, double b) { return a + b; }
std::string sum(const std::string& a, const std::string& b) {
    return a + b;
}

sum(3, 5);          // int → 8
sum(3.1, 2.9);     // double → 6.0
sum("Hello", "!");   // string → "Hello!"

Overloading = same name, different parameters. The compiler chooses by the type of the arguments. It cannot differ only in the return. Widely used in the STL (e.g. std::abs for int/double).

Function pointers
int sum(int a, int b) { return a + b; }
int mult(int a, int b) { return a * b; }

// Pointer de func:
int (*op)(int, int) = &sum;
op(3, 4);  // 7
op = &mult;
op(3, 4);  // 12

// Com std::function (flexible):
#include <functional>
std::function<int(int,int)> f = sum;
f = [](int a, int b) { return a - b; };

Function pointers store the addresses of functions. Syntax: type (*name)(params). std::function is more flexible (accepts lambdas, binds). Used for callbacks and strategies.

Classes and Objects


10 cards
Class with constructor
class Person {
private:
    std::string name;
    int age;

public:
    // Constructor com list de initialization:
    Person(std::string n, int i)
        : name(std::move(n)), age(i) {}

    // Getters:
    const std::string& getName() const { return name; }
    int getAge() const { return age; }
};

Person p("Anna", 30);

The initialization list (: name(n), age(i)) is more efficient than assigning in the body. std::move avoids copying strings. const on the method promises not to modify the object.

Operator overloading
class Point {
public:
    double x, y;

    Point operator+(const Point& o) const {
        return {x + o.x, y + o.y};
    }

    bool operator==(const Point& o) const {
        return x == o.x && y == o.y;
    }

    friend std::ostream& operator<<(std::ostream& the, const Point& p) {
        return the << "(" << p.x << ", " << p.y << ")";
    }
};

Overload operator+, ==, << for natural usage. const at the end = does not modify. friend accesses privates (for <<). Allows p1 + p2 and cout << p.

Copy and move constructor
class Buffer {
    std::unique_ptr<int[]> data;
    size_t tam;
public:
    // Move constructor:
    Buffer(Buffer&& o) noexcept
        : data(std::move(o.data)), tam(o.tam) {
        o.tam = 0;
    }

    // Copy constructor:
    Buffer(const Buffer& o) : tam(o.tam),
        data(std::make_unique<int[]>(o.tam)) {
        std::copy(o.data.get(), o.data.get() + tam, data.get());
    }
};

The move constructor (&&) transfers resources without copying — fast. The copy constructor (const &) duplicates data. noexcept on the move is essential for vector to use move on resize.

Inheritance
class Animal {
protected:
    std::string name;
public:
    Animal(std::string n) : name(std::move(n)) {}
    virtual void speak() const = 0;  // puro virtual
    virtual ~Animal() = default;
};

class Dog : public Animal {
public:
    Dog(std::string n) : Animal(std::move(n)) {}
    void speak() const override {
        std::cout << name << ": Au!\n";
    }
};

public inherits the interface. virtual allows overriding. = 0 makes it pure virtual (abstract class). override confirms it overrides a base method. A virtual destructor is mandatory in base classes.

struct vs class
// struct: membros public por default
struct Point {
    double x, y;  // public
};

// class: membros privados por default
class Account {
    double balance;  // private
public:
    void deposit(double v) { balance += v; }
};

// Convention:
// struct → data simple (POD, DTOs)
// class  → logic + encapsulation

The only difference: struct has public members by default, class private. Convention: struct for simple data (no invariants), class for objects with logic. Technically interchangeable.

Interfaces (abstract class)
class ISerializable {
public:
    virtual ~ISerializable() = default;
    virtual std::string toJSON() const = 0;
    virtual void fromJSON(const std::string& json) = 0;
};

class User : public ISerializable {
public:
    std::string toJSON() const override {
        return R"({"name": ")" + name + R"("})";
    }
    void fromJSON(const std::string& json) override {
        // parse...
    }
};

An abstract class = interface: only pure virtual methods (= 0). It cannot be instantiated. Derived classes implement everything. A virtual destructor is mandatory. Equivalent to Java/C# interfaces.

Polymorphism
std::vector<std::unique_ptr<Animal>> animals;
animals.push_back(std::make_unique<Dog>("Rex"));
animals.push_back(std::make_unique<Cat>("Miau"));

// Dispatch dynamic:
for (const auto& a : animals) {
    a->speak();  // chama o override correto
}

// Downcast safe:
if (auto* dog = dynamic_cast<Dog*>(animals[0].get())) {
    dog->wagTail();
}

Polymorphism = calling the correct method via a base pointer. It requires virtual. unique_ptr manages memory automatically. dynamic_cast does a safe downcast (returns nullptr on failure).

static members
class Counter {
    static int total;  // shared by all
public:
    Counter() { total++; }
    ~Counter() { total--; }
    static int getTotal() { return total; }
};

// Define outside the class:
int Counter::total = 0;

Counter a, b, c;
Counter::getTotal();  // 3

static members belong to the class, not to the object. Shared by all instances. Access via Class::member. Must be defined outside the class. Ideal for counters and constants.

Rule of Five
class Recurso {
    int* data;
public:
    Recurso(int n) : data(new int[n]) {}
    ~Recurso() { delete[] data; }                    // destructor
    Recurso(const Recurso& o);                        // copy
    Recurso& operator=(const Recurso& o);             // assignment copy
    Recurso(Recurso&& o) noexcept;                    // move
    Recurso& operator=(Recurso&& o) noexcept;         // assignment move
};
// Ou: Rule of Zero (usa smart pointers!)

If you define one, you probably need all 5: destructor, copy ctor, copy assign, move ctor, move assign. Rule of Zero: use unique_ptr/shared_ptr and define none.

Friend and encapsulation
class Secret {
    int code = 42;
    friend class Hacker;           // class amiga
    friend void reveal(Secret&); // func amiga
};

class Hacker {
public:
    void aceder(Secret& s) {
        std::cout << s.code;  // OK: is friend
    }
};

void reveal(Secret& s) {
    std::cout << s.code;  // OK: is friend
}

friend grants access to private members. It breaks encapsulation — use it sparingly. Useful for << operators, unit tests and tightly-coupled classes. It is neither transitive nor inherited.

STL Containers


10 cards
std::vector
#include <vector>

std::vector<int> v = {1, 2, 3};
v.push_back(4);       // adds at the end
v.size();             // 4
v[0];                 // 1 (without bounds check)
v.at(0);              // 1 (with bounds check)
v.front(); v.back();  // first / last
v.pop_back();         // remove last
v.erase(v.begin() + 1);  // remove index 1
v.clear();            // remove all
v.empty();            // true

vector is the most used container — a contiguous dynamic array. push_back adds in amortized O(1). at() checks bounds (throws an exception). erase removes by iterator. Reserve with reserve() for performance.

std::list
#include <list>

std::list<int> l = {3, 1, 4};
l.push_front(0);
l.push_back(5);
l.sort();            // sorts (not usa std::sort)
l.unique();          // remove duplicates consecutivos
l.reverse();

// Insertion/removal O(1) em any position
// (com iterator):
auto it = std::next(l.begin(), 2);
l.insert(it, 99);
l.erase(it);

list is a doubly linked list. Insertion/removal is O(1) with an iterator. No index access. sort() and splice() are its own methods. Use it for frequent insertions in the middle. Rarely needed.

Iterators
std::vector<int> v = {10, 20, 30};

// Iterator classic:
for (auto it = v.begin(); it != v.end(); ++it) {
    std::cout << *it << " ";
}

// reverse_iterator:
for (auto it = v.rbegin(); it != v.rend(); ++it) {
    std::cout << *it << " ";  // 30 20 10
}

// Advance N positions:
auto it = v.begin();
std::advance(it, 2);  // points to 30
auto dist = std::distance(v.begin(), it);  // 2

Iterators point to elements. begin()/end() delimit the range. * dereferences. rbegin/rend for reverse order. std::advance moves N positions. They are the base of all STL somethingrithms.

std::map and unordered_map
#include <map>
#include <unordered_map>

// map: sorted by key (tree)
std::map<std::string, int> grades;
grades["ana"] = 95;
grades["joao"] = 88;

// unordered_map: hash (faster)
std::unordered_map<std::string, int> cache;
cache["key"] = 42;

// Safe access:
if (auto it = grades.find("ana"); it != grades.end()) {
    std::cout << it->second;  // 95
}

map keeps keys sorted (O(log n)). unordered_map uses a hash (average O(1)). operator[] creates if it does not exist. find + end() for safe access. at() throws an exception if it does not exist.

std::stack and std::queue
#include <stack>
#include <queue>

// Stack (LIFO):
std::stack<int> stack;
stack.push(1); stack.push(2); stack.push(3);
stack.top();    // 3
stack.pop();    // remove 3
stack.size();   // 2

// Queue (FIFO):
std::queue<int> queue;
queue.push(1); queue.push(2); queue.push(3);
queue.front();   // 1
queue.back();    // 3
queue.pop();     // remove 1

stack = LIFO (last in, first out). queue = FIFO (first in, first out). They are adapters (they use deque underneath). No iterators. Ideal for DFS/BFS and undo/redo.

std::array and std::span
#include <array>
#include <span>  // C++20

std::array<int, 5> arr = {1, 2, 3, 4, 5};
arr.size();   // 5 (compile-time)
arr.fill(0);  // all a 0

// span: view about sequence contiguous (C++20)
void process(std::span<int> data) {
    for (int x : data) std::cout << x;
}

process(arr);              // de array
std::vector<int> v = {1,2};
process(v);                // de vector
process({arr.data(), 3});  // sub-range

std::array is fixed in size (compile-time), no heap. std::span (C++20) is a lightweight view over contiguous data (array, vector, C-array). No ownership. It replaces the pointer+size pair.

std::set and unordered_set
#include <set>
#include <unordered_set>

std::set<int> s = {3, 1, 4, 1, 5};
// s = {1, 3, 4, 5} — unique e sorted

s.insert(9);
s.count(3);    // 1 (exists)
s.erase(1);
s.size();      // 4

// unordered_set: hash, without order
std::unordered_set<std::string> tags;
tags.insert("cpp");
tags.contains("cpp");  // true (C++20)

set stores unique, sorted values. unordered_set is faster (hash). insert ignores duplicates. contains (C++20) checks presence. Ideal for checking existence and removing duplicates.

std::priority_queue
#include <queue>

// Max-heap (largest no top):
std::priority_queue<int> pq;
pq.push(3); pq.push(1); pq.push(4);
pq.top();   // 4 (largest)
pq.pop();   // remove 4

// Min-heap:
std::priority_queue<int, std::vector<int>,
    std::greater<int>> min_pq;
min_pq.push(3); min_pq.push(1);
min_pq.top();  // 1 (smallest)

priority_queue is a heap — the top is always the largest (max-heap). For a min-heap use std::greater. push/pop in O(log n). Ideal for scheduling, Dijkstra, top-K elements.

std::deque
#include <deque>

std::deque<int> d = {2, 3, 4};
d.push_front(1);   // {1, 2, 3, 4}
d.push_back(5);    // {1, 2, 3, 4, 5}
d.pop_front();     // remove 1
d.pop_back();      // remove 5
d[2];              // access by index

// vs vector:
// + push_front O(1)
// - without memory contiguous
// - slower iterators

deque = double-ended queue. push_front and push_back in O(1). No contiguous memory (blocks). Use it when you need to insert/remove at both ends. It is the base of stack and queue.

std::pair and std::tuple
#include <utility>
#include <tuple>

auto p = std::make_pair("Anna", 30);
p.first;   // "Anna"
p.second;  // 30

// Structured binding (C++17):
auto [name, age] = p;

// Tuple (more than 2):
auto t = std::make_tuple(1, "ok", 3.14);
auto [a, b, c] = t;
std::get<0>(t);  // 1

// tie (assign to existing):
int x; std::string s;
std::tie(x, s) = std::make_tuple(42, "hi");

pair groups 2 values. tuple groups N. structured binding destructures elegantly. std::get<N> accesses by index. Multiple function returns use pair/tuple.

Pointers and Memory


10 cards
Basic pointers
int x = 42;
int* p = &x;      // p stores address de x
std::cout << *p;  // 42 (dereferencing)
*p = 100;         // modifica x
std::cout << x;   // 100

int* null = nullptr;  // pointer null (C++11)

// Pointer to pointer:
int** pp = &p;
**pp;  // 42 → 100

& gets the address. * dereferences (accesses the value). nullptr is the modern null pointer (replaces NULL). Always initialize pointers. Check before dereferencing.

std::shared_ptr
#include <memory>

auto sp1 = std::make_shared<Resource>();
{
    auto sp2 = sp1;  // shares (ref count = 2)
    auto sp3 = sp1;  // ref count = 3
}
// sp2, sp3 destroyed → ref count = 1

sp1.use_count();  // 1
sp1.reset();      // releases (ref count = 0)

// Cost: atomic ref counting (slower than unique)
// Use when several owners need the same object

shared_ptr = shared ownership with reference counting. It frees when the count = 0. It is copyable (increments the count). It has atomic ops overhead. Use it when multiple owners share the same resource. Prefer unique_ptr if possible.

Dynamic arrays and C-arrays
// C-array (avoid):
int arr[5] = {1, 2, 3, 4, 5};
int* dyn = new int[n];
delete[] dyn;

// Moderno (prefer):
std::vector<int> v(n);           // dynamic
std::array<int, 5> a = {};      // fixo, stack
auto up = std::make_unique<int[]>(n);  // heap, RAII

// Pointer + size → span (C++20):
void process(std::span<int> data);
process(v);
process({arr, 5});

Avoid C-arrays and new[]. vector for dynamic size. std::array for fixed. make_unique<T[]> if you need heap with ownership. std::span (C++20) the a universal parameter.

References
int x = 10;
int& ref = x;   // alias to x
ref = 20;       // x now is 20

// Not can be null nor reassigned:
// int& r;      // ERROR: must initialize
// &ref = &y;   // impossible

// Em parameters:
void swap(int& a, int& b) {
    std::swap(a, b);
}

// Reference constant:
const int& cr = x;  // only reading

A reference = alias for an existing variable. It must be initialized. It cannot be null nor reassigned. const & prevents modification. Prefer references over pointers when null is not an option.

std::weak_ptr
#include <memory>

std::weak_ptr<Recurso> weak;
{
    auto strong = std::make_shared<Recurso>();
    weak = strong;

    if (auto sp = weak.lock()) {
        sp->usar();  // OK: still exists
    }
}
// strong destroyed

if (weak.expired()) {
    // object already not exists
}
auto sp = weak.lock();  // nullptr

weak_ptr observes without owning (it does not increment the ref count). It resolves reference cycles. lock() tries to obtain a shared_ptr (nullptr if expired). expired() checks. Essential for caches and observers.

lock_guard and resources
#include <mutex>

std::mutex mtx;
int partilhado = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);
    partilhado++;
    // unlock automatic ao exit do scope
}

// C++17 (deduction de type):
void other() {
    std::lock_guard lock(mtx);
    partilhado += 10;
}

// scoped_lock (multiple mutexes):
void multi() {
    std::scoped_lock lock(mtx1, mtx2);
}

lock_guard applies RAII to mutexes: lock in the constructor, unlock in the destructor. Impossible to forget the unlock. scoped_lock (C++17) locks multiple mutexes without deadlock. The standard pattern for thread safety.

new and delete (avoid)
// Allocation manual:
int* p = new int(42);
delete p;         // release
p = nullptr;      // avoid dangling

// Array:
int* arr = new int[100];
delete[] arr;     // delete[] to arrays!

// Object:
auto* obj = new Person("Anna");
delete obj;

// ⚠️ Risks: memory leaks, double free,
// dangling pointers. PREFERIR smart pointers!

new allocates on the heap, delete frees. delete[] for arrays. If you forget the delete = memory leak. If you delete twice = crash. In modern C++, always use unique_ptr/shared_ptr.

RAII
// RAII: Resource Acquisition Is Initialization
class File {
    FILE* f;
public:
    File(const char* path) {
        f = fopen(path, "r");
        if (!f) throw std::runtime_error("error");
    }
    ~File() {
        if (f) fclose(f);  // releases in destructor
    }
    // Not copyable:
    File(const File&) = delete;
};

// Use:
{
    File f("data.txt");
    // usar f...
}  // fclose automatic here!

RAII = acquire the resource in the constructor, release it in the destructor. It guarantees cleanup even with exceptions. It is the base of unique_ptr, lock_guard, fstream. The most important pattern in C++.

std::unique_ptr
#include <memory>

// Ownership exclusiva (not copyable):
auto p = std::make_unique<Person>("Anna", 30);
p->getName();  // "Anna"

// Transferir ownership (move):
auto p2 = std::move(p);
// p now is nullptr!

// Array:
auto arr = std::make_unique<int[]>(100);
arr[0] = 42;

// Liberta automatically ao exit do scope
// Sem need de delete!

unique_ptr = exclusive ownership. It cannot be copied, only moved (std::move). It frees memory automatically (RAII). make_unique (C++14) creates it safely. It replaces new/delete in most cases.

Move semantics
#include <utility>

std::vector<int> create() {
    return {1, 2, 3, 4, 5};  // move (RVO)
}

auto v1 = create();
auto v2 = std::move(v1);  // transfere buffer
// v1 now is empty (moved-from)

// Rvalue reference:
void process(std::string&& s) {
    data_ = std::move(s);  // steals resources
}

process(std::string("temporary"));  // OK
// std::string name = "x";
// process(std::move(name));  // move explicit

std::move converts to an rvalue (allows stealing resources). && is an rvalue reference. It avoids expensive copies. After a move, the object is in a valid but unspecified state. RVO optimizes returns automatically.

Templates


10 cards
Template function
template <typename T>
T largest(T a, T b) {
    return (a > b) ? a : b;
}

largest(3, 7);         // int → 7
largest(3.14, 2.71);   // double → 3.14
largest<std::string>("a", "b");  // explicit

// Multiple types:
template <typename T, typename U>
auto sum(T a, U b) {
    return a + b;
}

template <typename T> creates a generic function. The compiler generates code for each type used. T is substituted at compile time. Zero overhead vs manual code. The base of generic programming in C++.

Concepts (C++20)
#include <concepts>

// Usar concept existing:
template <std::integral T>
T sum(T a, T b) { return a + b; }

template <std::floating_point T>
T root(T x) { return std::sqrt(x); }

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

void print(const Printable auto& x) {
    std::cout << x;
}

Concepts (C++20) constrain templates with clear requirements. Readable compile errors. std::integral, std::floating_point are built-in. requires defines custom ones. It replaces SFINAE.

CRTP
// Curiously Recurring Template Pattern
template <typename Derived>
class Base {
public:
    void interface() {
        // "Polymorphism" static (without virtual):
        static_cast<Derived*>(this)->implementation();
    }
};

class Concrete : public Base<Concrete> {
public:
    void implementation() {
        std::cout << "Done!\n";
    }
};

Concrete c;
c.interface();  // "Done!" — resolvido em compilation

CRTP = a class derives from a template of itself. It enables static polymorphism (no vtable, faster). Used for mix-ins, counters and compile-time interfaces. An advanced but powerful pattern.

Template class
template <typename T>
class Box {
    T value;
public:
    Box(T v) : value(std::move(v)) {}
    T get() const { return value; }
    void set(T v) { value = std::move(v); }
};

Box<int> c(42);
Box<std::string> s("hello");

// C++17: CTAD (deduction automatic):
Box c2(42);       // deduz Box<int>
Box s2("hello"s);   // deduz Box<std::string>

Template classes parametrize types. Instantiate with Box<int>. CTAD (C++17) deduces the type from the constructor. The STL is entirely template-based: vector<T>, map<K,V>.

Variadic templates
// Number variable de arguments:
template <typename... Args>
void print(Args... args) {
    (std::cout << ... << args) << "\n";
}

print(1, " + ", 2, " = ", 3);

// Fold expressions (C++17):
template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // fold right
}

sum(1, 2, 3, 4);  // 10

// sizeof... account:
template <typename... Ts>
constexpr size_t count() { return sizeof...(Ts); }

Variadic templates accept N types. Args... is a parameter pack. A fold expression ((args + ...)) expands elegantly. sizeof... counts the elements. The base of std::tuple, make_unique, and a safe printf.

if constexpr
template <typename T>
std::string toStr(const T& val) {
    if constexpr (std::is_same_v<T, std::string>) {
        return val;  // already is string
    } else if constexpr (std::is_arithmetic_v<T>) {
        return std::to_string(val);
    } else {
        return val.toString();  // tem method
    }
}

toStr(42);            // "42"
toStr(std::string("oi")); // "oi"

// Non-compiled branches are discarded!

if constexpr (C++17) evaluates at compile time. False branches are discarded (they do not need to compile). It removes the need for SFINAE/tag dispatch. Ideal for generic code with per-type branches.

Specialization
// Template generic:
template <typename T>
struct TypeInfo {
    static std::string name() { return "unknown"; }
};

// Full specialization:
template <>
struct TypeInfo<int> {
    static std::string name() { return "integer"; }
};

template <>
struct TypeInfo<double> {
    static std::string name() { return "real"; }
};

TypeInfo<int>::name();     // "integer"
TypeInfo<char>::name();    // "unknown"

Full specialization (template <>) replaces the template for a specific type. The compiler chooses the most specific version. Used in type traits and metaprogramming.

Type traits
#include <type_traits>

// Check at compile time:
static_assert(std::is_integral_v<int>);
static_assert(std::is_floating_point_v<double>);
static_assert(std::is_same_v<int, int32_t>);

// Transform types:
using T1 = std::remove_const_t<const int>;  // int
using T2 = std::remove_pointer_t<int*>;     // int
using T3 = std::add_lvalue_reference_t<int>; // int&

// Condicional:
template <typename T>
using Storage = std::conditional_t<
    sizeof(T) <= 8, T, const T&>;

Type traits query and transform types at compile time. _v = boolean value, _t = resulting type. static_assert fails compilation if false. Essential for metaprogramming and SFINAE.

Partial specialization
// Generic:
template <typename T>
struct Container { using type = T; };

// Partial specialization to pointers:
template <typename T>
struct Container<T*> {
    using type = T;  // remove o *
};

// To vectors:
template <typename T>
struct Container<std::vector<T>> {
    using type = T;  // element type
};

Container<int*>::type;              // int
Container<std::vector<double>>::type; // double

Partial specialization specializes a pattern (e.g. all pointers). Only for classes/structs (not functions). The T* pattern captures any pointer. The base of type traits like remove_pointer.

SFINAE
// enable_if: only compila se condition for true
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
half(T x) { return x / 2; }

// C++20 (prefer concepts):
template <std::integral T>
T half(T x) { return x / 2; }

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

SFINAE = Substitution Failure Is Not An Error. It removes invalid overloads silently. enable_if is the classic form. C++17/20 offer better alternatives: if constexpr and concepts.

Modern C++


10 cards
auto and decltype
auto x = 42;            // int
auto s = std::string("hello");  // std::string
auto& ref = x;          // int&
const auto& cr = x;    // const int&

// decltype: type de uma expression
decltype(x) y = 10;    // int
decltype(x + 1.0) z;   // double

// decltype(auto) (C++14):
auto getVec() { return std::vector<int>{1,2,3}; }
decltype(auto) first(std::vector<int>& v) {
    return v[0];  // int& (preserva reference)
}

auto infers the type (avoids repetition). decltype gets the type of an expression. decltype(auto) preserves references and cv-qualifiers. Use auto for iterators and long types. Don't overuse it for obvious types.

std::string_view (C++17)
#include <string_view>

// Lightweight view (without copy):
void process(std::string_view sv) {
    sv.substr(0, 5);
    sv.size();
    sv.starts_with("Hello");  // C++20
}

process("Hello World");         // from literal
std::string s = "test";
process(s);                    // from string
process(std::string_view(s.data(), 3)); // sub

// ⚠️ Does not own the data!
// Do not keep it if the source can die

string_view is a read-only view over existing chars (no copy). It accepts literals, strings, substrings. Zero allocation. Careful: it is not an owner — if the source dies, it becomes dangling. Ideal for function parameters.

Coroutines (C++20)
#include <coroutine>
#include <generator>  // C++23

// Generator simple (C++23):
std::generator<int> fibonacci() {
    int a = 0, b = 1;
    while (true) {
        co_yield a;
        auto tmp = a;
        a = b;
        b = tmp + b;
    }
}

for (int x : fibonacci()) {
    if (x > 100) break;
    std::cout << x << " ";
}
// 0 1 1 2 3 5 8 13 21 34 55 89

Coroutines (C++20) are functions that can suspend (co_yield, co_await, co_return). std::generator (C++23) simplifies them. Lazy, efficient for infinite sequences and asynchronous I/O.

Structured bindings (C++17)
// De pair:
auto [name, age] = std::make_pair("Anna", 30);

// De tuple:
auto [x, y, z] = std::make_tuple(1.0, 2.0, 3.0);

// De struct:
struct Point { double x, y; };
auto [px, py] = Point{3.0, 4.0};

// Em range-for:
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
for (const auto& [key, val] : m) {
    std::cout << key << " = " << val << "\n";
}

Structured bindings destructure pairs, tuples, arrays and structs. auto [a, b] = ... creates local variables. It works in range-for for maps. More readable than .first/.second.

Ranges (C++20)
#include <ranges>
namespace views = std::views;

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

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

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

// Lazy evaluation: nothing is calculado up to iterate

Ranges (C++20) compose operations into pipelines with |. Lazy evaluation (only computes when iterating). filter, transform, take are views. It replaces loops + temporary vectors. Inspired by Python/Rust.

Designated initializers (C++20)
struct Config {
    int width = 800;
    int height = 600;
    bool fullscreen = false;
    std::string title = "App";
};

// C++20:
Config cfg{
    .width = 1920,
    .height = 1080,
    .fullscreen = true,
    // .title usa default "App"
};

// Order must match the declaration order
// Fields omitidos usam default
Config minimal{.title = "Game"};

Designated initializers (C++20) name the fields at initialization. The order must follow the declaration. Omitted fields use defaults. More readable than positional initialization. It works with structs and aggregate classes.

std::optional (C++17)
#include <optional>

std::optional<int> find(int id) {
    if (id > 0) return id * 10;
    return std::nullopt;
}

auto result = find(5);
if (result.has_value()) {
    std::cout << *result;  // 50
}

// Value_or (fallback):
int val = find(-1).value_or(0);  // 0

// Em parameters (optional explicit):
void config(std::optional<int> timeout = std::nullopt);

std::optional = a value that may not exist (without sentinels like -1). has_value() checks, * accesses. value_or gives a fallback. nullopt = empty. It replaces null pointers for optionals.

std::chrono
#include <chrono>
using namespace std::chrono;

// Measure time:
auto start = steady_clock::now();
doSomething();
auto end = steady_clock::now();
auto ms = duration_cast<milliseconds>(end - start);
std::cout << ms.count() << " ms\n";

// Literais (C++14):
using namespace std::chrono_literals;
auto timeout = 5s;
auto delay = 100ms;
std::this_thread::sleep_for(delay);

// Timestamp:
auto now = system_clock::now();
auto epoch = now.time_since_epoch();

std::chrono handles time with type safety. steady_clock for measurements (monotonic). duration_cast converts units. Literals 5s, 100ms (C++14). It replaces clock() and C APIs.

std::variant (C++17)
#include <variant>

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

std::get<std::string>(v);       // "hello"
std::get<2>(v);                 // por index
v.index();                      // 2 (type active)
std::holds_alternative<int>(v); // false

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

std::variant = a union with type safety. It holds ONE of N types. std::get accesses it (throws an exception if the type is wrong). std::visit applies a visitor. A safe replacement for C unions. No heap allocation.

std::filesystem (C++17)
#include <filesystem>
namespace fs = std::filesystem;

fs::path p = "/home/user/file.txt";
p.filename();    // "file.txt"
p.extension();   // ".txt"
p.parent_path(); // "/home/user"

fs::exists(p);
fs::file_size(p);
fs::create_directories("a/b/c");
fs::copy("orig.txt", "dest.txt");
fs::remove("temp.txt");

// Iterate directory:
for (const auto& entry : fs::directory_iterator(".")) {
    std::cout << entry.path() << "\n";
}

std::filesystem (C++17) manipulates files and folders portably. path composes/decomposes paths. exists, copy, remove, create_directories. It replaces OS APIs.

Compilation and Tips


10 cards
Compiling with g++ / clang++
# Basic:
g++ main.cpp -o app

# Com standard e warnings:
g++ -std=c++20 -Wall -Wextra -O2 main.cpp -o app

# Clang:
clang++ -std=c++20 -Wall main.cpp -o app

# Multiple files:
g++ -std=c++20 main.cpp utils.cpp -o app

# Execute:
./app

g++ and clang++ are the main compilers. -std=c++20 sets the standard. -Wall -Wextra enables warnings. -O2 optimizes. Always compile with warnings enabled.

Preprocessor
#define MAX_SIZE 1024
#define SQUARE(x) ((x) * (x))

#ifdef DEBUG
    #define LOG(msg) std::cout << "[DBG] " << msg << "\n"
#else
    #define LOG(msg)
#endif

#if __cplusplus >= 202002L
    // code C++20
#endif

#pragma once  // header guard

#undef MAX_SIZE  // remove macro

The preprocessor runs before compilation. #define creates macros (textual substitution). #ifdef conditional compilation. #pragma once prevents double inclusion. Prefer constexpr over macros.

Modern best practices
// ✅ DO:
// • Smart pointers (unique_ptr, shared_ptr)
// • const on everything that does not change
// • const reference to parameters
// • auto for long types/iterators
// • Range-for instead of indexes
// • [[nodiscard]] on important returns

[[nodiscard]] int calculate() { return 42; }

// ❌ AVOID:
// • new/delete manual
// • using namespace std in headers
// • C-arrays and C-strings
// • Macros (prefer constexpr)
// • Raw pointers the owners

Golden rules: smart pointers instead of new/delete, const by default, const & for parameters, range-for to iterate. [[nodiscard]] warns if the return is ignored. Follow the C++ Core Guidelines.

Basic CMake
cmake_minimum_required(VERSION 3.20)
project(MeuProjeto LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(app
    src/main.cpp
    src/utils.cpp
)

target_include_directories(app PRIVATE include)

# Build:
# mkdir build && cd build
# cmake .. && cmake --build .

CMake is the de facto standard build system. add_executable defines the target. target_include_directories adds headers. Out-of-source build in build/. Cross-platform (Linux, Win, Mac).

Sanitizers
# Address Sanitizer (memory errors):
g++ -fsanitize=address -g main.cpp -o app

# Undefined Behavior Sanitizer:
g++ -fsanitize=undefined -g main.cpp -o app

# Thread Sanitizer (data races):
g++ -fsanitize=thread -g main.cpp -o app

# Memory Sanitizer (uninitialized reads):
clang++ -fsanitize=memory -g main.cpp -o app

# Combine (exceto thread):
g++ -fsanitize=address,undefined -g main.cpp -o app

Sanitizers detect bugs at runtime. address = buffer overflow, use-after-free. undefined = signed overflow, null deref. thread = data races. -g for stack traces. Always use them in tests.

Linking and libraries
# Compile to object file:
g++ -c -std=c++20 utils.cpp -o utils.o

# Create static library:
ar rcs libutils.a utils.o

# Create shared library:
g++ -shared -fPIC utils.o -o libutils.so

# Link:
g++ main.cpp -L. -lutils -o app

# CMake (automatic):
# add_library(utils STATIC src/utils.cpp)
# target_link_libraries(app PRIVATE utils)

-c compiles without linking (produces .o). ar creates a static library (.a). -shared -fPIC creates a dynamic one (.so/.dll). -L sets the path, -l links. CMake handles everything automatically.

Headers and includes
// main.h (header):
#pragma once  // header guard

#include <string>
#include <vector>

namespace app {
    void start(const std::string& name);
    std::vector<int> generate(int n);
}

// main.cpp (implementation):
#include "main.h"
#include <iostream>

void app::start(const std::string& name) {
    std::cout << "Hello " << name << "\n";
}

#pragma once prevents double inclusion. Headers declare, .cpp implement. #include "file.h" for local ones, <file> for system ones. Minimize includes in headers (forward declare when possible).

Debugging with GDB
# Compile com debug info:
g++ -g -O0 main.cpp -o app

# Start GDB:
gdb ./app

# Comandos:
(gdb) break main         # breakpoint
(gdb) break utils.cpp:42 # breakpoint em line
(gdb) run                # execute
(gdb) next               # step over
(gdb) step               # step into
(gdb) print variable     # ver value
(gdb) backtrace          # call stack
(gdb) continue           # continue

GDB is the standard debugger. Compile with -g -O0 (no optimization). break sets breakpoints. next/step advance. print inspects variables. backtrace shows the call stack.

Namespaces
namespace app::utils {
    int sum(int a, int b) { return a + b; }
}

// Usar:
int r = app::utils::sum(3, 4);

// using (scope limitado):
void func() {
    using namespace app::utils;
    sum(1, 2);  // OK here
}

// Alias:
namespace au = app::utils;
au::sum(5, 6);

// NEVER: using namespace std; in headers!

namespace organizes code and avoids collisions. :: accesses members. using namespace imports (avoid it in headers!). An alias (namespace au = ...) simplifies. C++17 allows nested app::utils.

Exceptions
#include <stdexcept>

void process(int x) {
    if (x < 0) {
        throw std::invalid_argument("x negative");
    }
}

try {
    process(-1);
} catch (const std::invalid_argument& e) {
    std::cerr << "Error: " << e.what() << "\n";
} catch (const std::exception& e) {
    std::cerr << "Error: " << e.what() << "\n";
} catch (...) {
    std::cerr << "Error unknown\n";
}

throw raises an exception. try/catch catches it. std::exception is the base. what() gives the message. catch(...) catches everything. RAII guarantees cleanup. Some projects forbid exceptions (performance).

STL Algorithms


10 cards
std::sort
#include <somethingrithm>

std::vector<int> v = {5, 2, 8, 1, 9};

// Order crescente:
std::sort(v.begin(), v.end());
// {1, 2, 5, 8, 9}

// Order decrescente:
std::sort(v.begin(), v.end(), std::greater<int>());

// Comparator custom:
std::sort(v.begin(), v.end(),
    [](int a, int b) { return a % 3 < b % 3; });

// Stable (keeps relative order):
std::stable_sort(v.begin(), v.end());

std::sort sorts in O(n log n). It accepts a custom comparator (lambda or functor). std::greater reverses. stable_sort keeps the order of equal elements. It only works on containers with random access.

std::for_each
#include <somethingrithm>

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

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

// With state (functor):
struct Printer {
    int count = 0;
    void operator()(int x) {
        std::cout << ++count << ": " << x << "\n";
    }
};
std::for_each(v.begin(), v.end(), Printer{});

std::for_each applies a function/lambda to each element. It can modify with int&. It accepts functors with state. In C++11+, range-for is generally preferred. for_each_n (C++17) for N elements.

remove and unique
#include <somethingrithm>

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

// Remove all the 2s (idiom erase-remove):
v.erase(
    std::remove(v.begin(), v.end(), 2),
    v.end());
// v = {1, 3, 4}

// Remove duplicates (requires sorted):
std::sort(v.begin(), v.end());
v.erase(std::unique(v.begin(), v.end()), v.end());

// C++20 (simpler):
std::erase(v, 2);          // remove all the 2s
std::erase_if(v, [](int x) { return x > 3; });

The erase-remove idiom: std::remove shifts elements and returns the new end; erase resizes. C++20 simplifies this with std::erase/std::erase_if. unique removes consecutive duplicates.

std::find and search
#include <somethingrithm>

std::vector<int> v = {10, 20, 30, 40};

// Find value:
auto it = std::find(v.begin(), v.end(), 30);
if (it != v.end()) {
    std::cout << "Found: " << *it;
}

// Find com predicado:
auto it2 = std::find_if(v.begin(), v.end(),
    [](int x) { return x > 25; });

// Count:
int n = std::count(v.begin(), v.end(), 20);  // 1
int m = std::count_if(v.begin(), v.end(),
    [](int x) { return x > 15; });  // 3

std::find searches by value (O(n)). find_if uses a predicate. It returns an iterator (compare with end()). count/count_if count occurrences. On map/set, use the container's .find() (O(log n)).

min, max and clamp
#include <somethingrithm>

int a = std::min(3, 7);    // 3
int b = std::max(3, 7);    // 7
auto [mn, mx] = std::minmax({5, 2, 8, 1});

// Min/max de range:
std::vector<int> v = {4, 2, 9, 1};
auto smallest = *std::min_element(v.begin(), v.end());
auto largest = *std::max_element(v.begin(), v.end());

// Clamp (limit to range):
int c = std::clamp(15, 0, 10);  // 10
int d = std::clamp(-5, 0, 10);  // 0

std::min/std::max compare 2 values. min_element/max_element return an iterator to the extreme of the range. std::clamp (C++17) limits a value to [min, max].

partition and nth_element
#include <somethingrithm>

std::vector<int> v = {5, 2, 8, 1, 9, 3};

// Partition (even before de odd):
auto it = std::partition(v.begin(), v.end(),
    [](int x) { return x % 2 == 0; });
// [2, 8] | [5, 1, 9, 3]

// Stable:
std::stable_partition(v.begin(), v.end(), pred);

// N-th element (O(n) average):
std::nth_element(v.begin(), v.begin() + 2, v.end());
// v[2] is the 3rd smallest (rest not sorted)

// Mediana:
std::nth_element(v.begin(), v.begin() + v.size()/2, v.end());

std::partition splits into 2 groups (predicate true/false). nth_element places the Nth element in its correct position in O(n) — ideal for median and top-K without sorting everything.

std::transform
#include <somethingrithm>

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

// Transform each element:
std::transform(v.begin(), v.end(), result.begin(),
    [](int x) { return x * x; });
// result = {1, 4, 9, 16, 25}

// In-place:
std::transform(v.begin(), v.end(), v.begin(),
    [](int x) { return x * 2; });

// Two inputs:
std::transform(a.begin(), a.end(), b.begin(),
    result.begin(), std::plus<int>());

std::transform applies a function to each element and stores the result. It can be in-place (output = input). The two-range version combines elements. Similar to map in functional languages.

binary_search and lower_bound
#include <somethingrithm>

std::vector<int> v = {1, 3, 5, 7, 9, 11};
// MUST be sorted!

// Exists?
bool exists = std::binary_search(v.begin(), v.end(), 7);

// Insertion position:
auto lb = std::lower_bound(v.begin(), v.end(), 6);
// points to 7 (first >= 6)

auto ub = std::upper_bound(v.begin(), v.end(), 7);
// points to 9 (first > 7)

// Range of equals:
auto [lo, hi] = std::equal_range(v.begin(), v.end(), 7);

binary_search checks existence in O(log n) (requires sorted). lower_bound = first ≥ value. upper_bound = first > value. equal_range gives both. Essential for efficient searching.

std::accumulate and reduce
#include <numeric>

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

// Sum:
int sum = std::accumulate(v.begin(), v.end(), 0);
// 15

// Product:
int prod = std::accumulate(v.begin(), v.end(), 1,
    std::multiplies<int>());
// 120

// Com lambda:
auto concat = std::accumulate(v.begin(), v.end(),
    std::string(""), [](std::string acc, int x) {
        return acc + std::to_string(x) + ",";
    });
// "1,2,3,4,5,"

std::accumulate reduces a range to a single value. It starts with an initial value. It accepts a custom operation (lambda). std::reduce (C++17) is parallel. Equivalent to functional reduce/fold.

copy, fill and generate
#include <somethingrithm>

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

// Copy:
std::copy(src.begin(), src.end(), dst.begin());

// Copy with condition:
std::copy_if(src.begin(), src.end(),
    std::back_inserter(dst),
    [](int x) { return x > 3; });

// Fill:
std::fill(dst.begin(), dst.end(), 0);

// Generate:
std::generate(dst.begin(), dst.end(),
    [n = 0]() mutable { return n++; });

std::copy copies a range. copy_if filters. back_inserter appends at the end. fill fills with a value. generate calls a function for each position. mutable allows modifying the lambda's capture.