Toolmingo
Guides17 min read

C++ Cheat Sheet: Modern C++ Syntax, STL & Patterns

A complete C++ cheat sheet covering modern C++17/20 syntax, STL containers, algorithms, smart pointers, templates, concurrency, and common patterns with copy-ready examples.

C++ is a high-performance, multi-paradigm language used in systems programming, game engines, embedded devices, and high-frequency trading. This cheat sheet covers modern C++17/20 — from basics to templates and concurrency — with copy-ready examples.

Quick reference

The 25 patterns that cover 95% of everyday C++ development.

Pattern Example
Auto type auto x = 42;
Range-for for (auto& v : vec)
Initialiser list vector<int> v = {1, 2, 3};
Structured binding auto [a, b] = pair;
Lambda [x](int y) { return x + y; }
Unique pointer auto p = make_unique<T>(args);
Shared pointer auto p = make_shared<T>(args);
Move semantics T t = std::move(other);
Perfect forward std::forward<T>(arg)
String literal std::string s = "hello"s;
Optional std::optional<int> opt = 42;
Variant std::variant<int, string>
Fold expression (args + ...)
If constexpr if constexpr (is_integral_v<T>)
Constexpr func constexpr int sq(int x)
Nodiscard [[nodiscard]] int compute();
Scoped enum enum class Color { Red, Green };
Nullptr T* p = nullptr;
Override void f() override;
Delete T(const T&) = delete;
Default T() = default;
Template alias using Vec = vector<T>;
Concept (C++20) template<std::integral T>
Span (C++20) std::span<int> s(arr, 5);
Range algo (C++20) ranges::sort(vec);

Variables and types

// Fundamental types
int         i = 42;
long long   ll = 9'000'000'000LL;   // digit separators (C++14)
double      d = 3.14;
float       f = 3.14f;
bool        b = true;
char        c = 'A';
std::string s = "hello";

// Auto — prefer for complex types
auto x = 3.14;                      // double
auto v = std::vector<int>{1, 2, 3};

// Const and constexpr
const int MAX = 100;                // runtime constant
constexpr int SQ = 5 * 5;          // compile-time constant

// References and pointers
int a = 10;
int& ref = a;        // lvalue reference — alias for a
int* ptr = &a;       // pointer to a
*ptr = 20;           // dereference

// Nullptr — always use instead of NULL or 0
int* p = nullptr;

// Structured bindings (C++17)
auto [x2, y2] = std::pair<int, int>{1, 2};
auto [key, val] = std::map<std::string, int>::value_type{"age", 30};

Type aliases

using Name = std::string;
using IntVec = std::vector<int>;
template<typename T>
using Matrix = std::vector<std::vector<T>>;

Strings

#include <string>
#include <string_view>

std::string s = "Hello, World!";

// Size / access
s.size();           // 13
s.length();         // same as size()
s[0];               // 'H'
s.at(0);            // 'H' (bounds-checked)
s.front();          // 'H'
s.back();           // '!'

// Modify
s += " More";
s.append(" text");
s.insert(5, " C++");
s.erase(5, 4);
s.replace(0, 5, "Hi");

// Search
s.find("World");    // returns position or string::npos
s.rfind("l");
s.contains("World"); // C++23; use find() != npos for C++17

// Substrings and splitting
s.substr(7, 5);     // "World"

// Conversions
std::to_string(42);
std::stoi("42");
std::stod("3.14");

// string_view — non-owning, cheap to copy (prefer for read-only params)
std::string_view sv = s;
sv.substr(0, 5);    // no allocation

Control flow

// If / else
if (x > 0) {
    // ...
} else if (x < 0) {
    // ...
} else {
    // ...
}

// If with initialiser (C++17)
if (auto it = map.find(key); it != map.end()) {
    std::cout << it->second;
}

// Switch
switch (day) {
    case 1: std::cout << "Mon"; break;
    case 2: std::cout << "Tue"; break;
    default: std::cout << "Other";
}

// Loops
for (int i = 0; i < 10; ++i) { }

// Range-for
std::vector<int> nums{1, 2, 3};
for (auto& n : nums) { n *= 2; }  // by reference — modifies elements
for (const auto& n : nums) { }    // by const reference — read-only

// While / do-while
while (cond) { }
do { } while (cond);

// Ternary
int abs_val = x >= 0 ? x : -x;

Functions

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

// Default parameters
void greet(std::string name, std::string greeting = "Hello") {
    std::cout << greeting << ", " << name;
}

// Overloading
int    square(int x)    { return x * x; }
double square(double x) { return x * x; }

// Inline — suggest inlining (compiler decides)
inline int max(int a, int b) { return a > b ? a : b; }

// Returning multiple values (use struct or pair/tuple)
std::pair<int, int> divmod(int a, int b) {
    return {a / b, a % b};
}
auto [quot, rem] = divmod(17, 5);

// Variadic templates
template<typename... Args>
void print(Args&&... args) {
    ((std::cout << args << " "), ...);  // fold expression (C++17)
}

// Lambdas
auto sq = [](int x) { return x * x; };
int factor = 3;
auto mul = [factor](int x) { return x * factor; };   // capture by value
auto inc = [&factor](int x) { return x + factor; };  // capture by reference
auto gen = [i = 0]() mutable { return i++; };        // mutable capture

Classes and OOP

class Animal {
public:
    Animal(std::string name, int age)   // constructor
        : name_(std::move(name)), age_(age) {}  // member initialiser list

    // Virtual destructor — always for base classes
    virtual ~Animal() = default;

    virtual std::string speak() const = 0;  // pure virtual → abstract class

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

    // Static member
    static int count() { return count_; }

protected:
    std::string name_;
    int age_;

private:
    inline static int count_ = 0;
};

class Dog : public Animal {
public:
    Dog(std::string name, int age) : Animal(std::move(name), age) {}

    std::string speak() const override { return "Woof!"; }
};

// Usage
auto d = std::make_unique<Dog>("Rex", 3);
std::cout << d->speak();

Special member functions

class MyClass {
public:
    MyClass() = default;                      // default constructor
    MyClass(const MyClass&) = delete;         // disable copy constructor
    MyClass& operator=(const MyClass&) = delete; // disable copy assignment
    MyClass(MyClass&&) noexcept = default;    // move constructor
    MyClass& operator=(MyClass&&) noexcept = default; // move assignment
    ~MyClass() = default;
};

Operator overloading

struct Vec2 {
    double x, y;
    Vec2 operator+(const Vec2& o) const { return {x + o.x, y + o.y}; }
    Vec2& operator+=(const Vec2& o) { x += o.x; y += o.y; return *this; }
    bool operator==(const Vec2& o) const = default;  // C++20
};
// Stream output
std::ostream& operator<<(std::ostream& os, const Vec2& v) {
    return os << "(" << v.x << ", " << v.y << ")";
}

Smart pointers

Always prefer smart pointers over raw new/delete.

#include <memory>

// unique_ptr — sole ownership, non-copyable
auto up = std::make_unique<std::string>("hello");
up->size();                      // dereference with ->
std::string* raw = up.get();     // raw pointer (does not transfer ownership)
up.reset();                      // destroy immediately

// shared_ptr — shared ownership, reference-counted
auto sp1 = std::make_shared<int>(42);
auto sp2 = sp1;                  // reference count = 2
sp1.use_count();                 // 2

// weak_ptr — non-owning observer; breaks circular references
std::weak_ptr<int> wp = sp1;
if (auto locked = wp.lock()) {   // obtain temporary shared_ptr
    std::cout << *locked;
}

// Passing smart pointers to functions
void takeOwnership(std::unique_ptr<Foo> p);    // transfers ownership
void borrow(const Foo& ref);                   // just needs to use it
void share(std::shared_ptr<Foo> p);            // shared ownership needed
Pointer Use when
unique_ptr One clear owner (default choice)
shared_ptr Multiple owners / shared lifecycle
weak_ptr Observer; breaks cycles with shared_ptr
Raw T* Non-owning reference (never new/delete manually)

STL containers

#include <vector>
#include <array>
#include <list>
#include <deque>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <stack>
#include <queue>

// --- vector (dynamic array) ---
std::vector<int> v = {3, 1, 4, 1, 5};
v.push_back(9);
v.emplace_back(2);           // construct in-place
v.pop_back();
v.size();                    // element count
v.empty();
v.reserve(100);              // pre-allocate capacity
v.resize(10, 0);             // resize, fill new with 0
v.front(); v.back();
v[2]; v.at(2);               // at() throws if out of range
v.erase(v.begin() + 2);
v.insert(v.begin(), 0);

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

// --- unordered_map (hash map) O(1) average ---
std::unordered_map<std::string, int> scores;
scores["Alice"] = 95;
scores.emplace("Bob", 80);
scores.contains("Alice");    // C++20
auto it = scores.find("Alice");
if (it != scores.end()) { std::cout << it->second; }
scores.erase("Bob");
for (auto& [k, v] : scores) { std::cout << k << "=" << v; }

// --- map (sorted tree) O(log n) ---
std::map<std::string, int> m;
m["a"] = 1;
m.lower_bound("b");          // iterator to first key >= "b"

// --- set / unordered_set ---
std::unordered_set<int> s = {1, 2, 3};
s.insert(4);
s.count(3);                  // 1 if present, 0 if not
s.erase(2);

// --- stack (LIFO) ---
std::stack<int> stk;
stk.push(1); stk.push(2);
stk.top();                   // 2
stk.pop();

// --- queue (FIFO) ---
std::queue<int> q;
q.push(1); q.push(2);
q.front();                   // 1
q.pop();

// --- priority_queue (max-heap by default) ---
std::priority_queue<int> pq;
pq.push(3); pq.push(1); pq.push(4);
pq.top();                    // 4
// Min-heap:
std::priority_queue<int, std::vector<int>, std::greater<int>> min_pq;

STL algorithms

#include <algorithm>
#include <numeric>

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

// Sort
std::sort(v.begin(), v.end());
std::sort(v.begin(), v.end(), std::greater<int>());  // descending
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

// Ranges (C++20) — no .begin()/.end() needed
std::ranges::sort(v);
std::ranges::sort(v, std::greater{});

// Search
auto it = std::find(v.begin(), v.end(), 3);
bool found = std::binary_search(v.begin(), v.end(), 3);  // sorted only
auto [mn, mx] = std::minmax_element(v.begin(), v.end());

// Transform / modify
std::transform(v.begin(), v.end(), v.begin(), [](int x) { return x * 2; });
std::for_each(v.begin(), v.end(), [](int& x) { x += 1; });
std::fill(v.begin(), v.end(), 0);
std::reverse(v.begin(), v.end());

// Copy / remove
std::vector<int> out;
std::copy_if(v.begin(), v.end(), std::back_inserter(out), [](int x) { return x > 2; });
// Erase-remove idiom (pre-C++20)
v.erase(std::remove_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }), v.end());
// C++20:
std::erase_if(v, [](int x) { return x % 2 == 0; });

// Numeric
int sum = std::accumulate(v.begin(), v.end(), 0);
int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());
std::partial_sum(v.begin(), v.end(), out.begin());
std::iota(v.begin(), v.end(), 1);  // fill with 1, 2, 3, ...

// Count / check
int cnt = std::count_if(v.begin(), v.end(), [](int x) { return x > 3; });
bool all = std::all_of(v.begin(), v.end(), [](int x) { return x > 0; });
bool any = std::any_of(v.begin(), v.end(), [](int x) { return x > 10; });
bool none = std::none_of(v.begin(), v.end(), [](int x) { return x < 0; });

Templates

// Function template
template<typename T>
T max_val(T a, T b) { return a > b ? a : b; }
max_val(3, 7);           // deduced: int
max_val(3.0, 7.0);       // deduced: double

// Class template
template<typename T>
class Stack {
public:
    void push(T val) { data_.push_back(std::move(val)); }
    T pop() {
        T val = std::move(data_.back());
        data_.pop_back();
        return val;
    }
    bool empty() const { return data_.empty(); }
private:
    std::vector<T> data_;
};

// Template specialisation
template<>
std::string max_val<std::string>(std::string a, std::string b) {
    return a.size() > b.size() ? a : b;
}

// Variadic templates
template<typename T>
T sum(T val) { return val; }

template<typename T, typename... Rest>
T sum(T first, Rest... rest) { return first + sum(rest...); }
sum(1, 2, 3, 4);  // 10

// Concepts (C++20) — constrain template parameters
template<std::integral T>
T gcd(T a, T b) { return b == 0 ? a : gcd(b, a % b); }

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

template<Printable T>
void log(const T& val) { std::cout << val << "\n"; }

// if constexpr — compile-time branching
template<typename T>
void process(T val) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << "int: " << val;
    } else if constexpr (std::is_floating_point_v<T>) {
        std::cout << "float: " << val;
    } else {
        std::cout << "other: " << val;
    }
}

Error handling

#include <stdexcept>
#include <optional>
#include <expected>  // C++23

// Exceptions
try {
    if (x < 0) throw std::invalid_argument("x must be >= 0");
    if (x > 100) throw std::out_of_range("x exceeds limit");
    // ...
} catch (const std::invalid_argument& e) {
    std::cerr << "Invalid argument: " << e.what();
} catch (const std::exception& e) {
    std::cerr << "Error: " << e.what();
} catch (...) {
    std::cerr << "Unknown error";
}

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

// Optional — value that may be absent (no exceptions, no null)
std::optional<int> find_value(const std::vector<int>& v, int target) {
    auto it = std::find(v.begin(), v.end(), target);
    if (it == v.end()) return std::nullopt;
    return *it;
}

auto result = find_value(nums, 42);
if (result) {
    std::cout << *result;
}
result.value_or(-1);    // -1 if empty

// std::expected (C++23) — explicit success/error without exceptions
std::expected<int, std::string> parse_int(std::string_view s) {
    try { return std::stoi(std::string(s)); }
    catch (...) { return std::unexpected("not a number: " + std::string(s)); }
}

auto val = parse_int("42");
if (val) { std::cout << *val; }
else { std::cout << val.error(); }

Move semantics and rvalue references

// lvalue = named object; rvalue = temporary / expiring value
std::string s1 = "hello";
std::string s2 = s1;             // copy — s1 still valid
std::string s3 = std::move(s1); // move — s1 is now in valid-but-unspecified state

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

    // Move constructor
    Buffer(Buffer&& other) noexcept
        : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
    }

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

    // Delete copy
    Buffer(const Buffer&) = delete;
    Buffer& operator=(const Buffer&) = delete;

private:
    int* data_;
    size_t size_;
};

// Perfect forwarding
template<typename T>
void wrapper(T&& arg) {
    target(std::forward<T>(arg));  // forwards lvalue as lvalue, rvalue as rvalue
}

Concurrency (C++11/14/17)

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

// Basic thread
std::thread t([]() { std::cout << "Hello from thread\n"; });
t.join();    // wait for completion
// t.detach();  // let it run independently

// Mutex — protect shared data
std::mutex mtx;
int counter = 0;

void safe_increment() {
    std::lock_guard<std::mutex> lock(mtx);  // RAII lock
    ++counter;
}

// unique_lock — more flexible (can unlock/relock)
std::unique_lock<std::mutex> lock(mtx);
lock.unlock();
// ...
lock.lock();

// Atomic — lock-free for simple types
std::atomic<int> atom_counter{0};
atom_counter.fetch_add(1, std::memory_order_relaxed);
int val = atom_counter.load();

// Condition variable
std::mutex cv_mtx;
std::condition_variable cv;
bool ready = false;

// Producer
{
    std::lock_guard<std::mutex> lk(cv_mtx);
    ready = true;
}
cv.notify_all();

// Consumer
std::unique_lock<std::mutex> lk(cv_mtx);
cv.wait(lk, []{ return ready; });  // spurious wakeup safe

// std::async / std::future
auto future = std::async(std::launch::async, []() {
    return compute_heavy_result();
});
// Do other work...
auto result = future.get();  // blocks until ready

// Multiple futures
std::vector<std::future<int>> futures;
for (int i = 0; i < 4; ++i) {
    futures.push_back(std::async(std::launch::async, [i]() { return i * i; }));
}
for (auto& f : futures) { std::cout << f.get() << " "; }

File I/O

#include <fstream>
#include <sstream>
#include <filesystem>

// Read entire file
std::ifstream f("data.txt");
if (!f) throw std::runtime_error("cannot open file");
std::string line;
while (std::getline(f, line)) {
    std::cout << line << "\n";
}

// Read all at once
std::ifstream file("data.txt");
std::string content((std::istreambuf_iterator<char>(file)),
                     std::istreambuf_iterator<char>());

// Write file
std::ofstream out("out.txt");
out << "Line 1\n";
out << "Line 2\n";
// File is closed automatically when out goes out of scope

// Append
std::ofstream app("log.txt", std::ios::app);
app << "New entry\n";

// String streams (in-memory I/O)
std::ostringstream oss;
oss << "Value: " << 42 << ", Pi: " << 3.14;
std::string result = oss.str();

std::istringstream iss("10 20 30");
int a, b, c;
iss >> a >> b >> c;

// std::filesystem (C++17)
namespace fs = std::filesystem;

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

fs::exists(p);
fs::is_directory("dir");
fs::create_directories("a/b/c");
fs::remove("file.txt");
fs::copy("src.txt", "dst.txt");
fs::rename("old.txt", "new.txt");
fs::file_size("data.bin");

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

Modern C++20 features

// Concepts — readable constraints
template<typename T>
concept Number = std::is_arithmetic_v<T>;

template<Number T>
T multiply(T a, T b) { return a * b; }

// Ranges — composable lazy views
#include <ranges>
namespace views = std::views;

std::vector<int> nums = {1, 2, 3, 4, 5, 6};
auto result = nums
    | views::filter([](int n) { return n % 2 == 0; })
    | views::transform([](int n) { return n * n; });
// result is lazy — evaluated only when iterated
for (int n : result) { std::cout << n << " "; }  // 4 16 36

// Span — non-owning view over contiguous data
#include <span>
void process(std::span<const int> data) {
    for (int x : data) std::cout << x;
}
std::vector<int> v = {1, 2, 3};
process(v);           // works with vector
int arr[] = {1, 2, 3};
process(arr);         // works with C array

// Three-way comparison (spaceship operator)
#include <compare>
struct Point {
    int x, y;
    auto operator<=>(const Point&) const = default;  // generates all 6 comparisons
};

// Coroutines (C++20) — stackless co-routines
#include <coroutine>
// Usually used through libraries (cppcoro, std::generator in C++23)

Common patterns

RAII (Resource Acquisition Is Initialisation)

// Resources tied to object lifetime — no leaks possible
class FileGuard {
public:
    explicit FileGuard(const char* path) : f_(std::fopen(path, "r")) {
        if (!f_) throw std::runtime_error("cannot open");
    }
    ~FileGuard() { if (f_) std::fclose(f_); }

    // Prevent copying
    FileGuard(const FileGuard&) = delete;
    FileGuard& operator=(const FileGuard&) = delete;

    FILE* get() { return f_; }
private:
    FILE* f_;
};

// Scope guard — run cleanup on scope exit
template<typename F>
class ScopeGuard {
public:
    explicit ScopeGuard(F f) : f_(std::move(f)) {}
    ~ScopeGuard() { f_(); }
private:
    F f_;
};
// Usage:
ScopeGuard cleanup([](){ std::cout << "cleanup!\n"; });

Builder pattern

class HttpRequest {
public:
    HttpRequest& url(std::string u) { url_ = std::move(u); return *this; }
    HttpRequest& method(std::string m) { method_ = std::move(m); return *this; }
    HttpRequest& header(std::string k, std::string v) {
        headers_[std::move(k)] = std::move(v); return *this;
    }
    void send() { /* ... */ }
private:
    std::string url_, method_ = "GET";
    std::unordered_map<std::string, std::string> headers_;
};

HttpRequest().url("https://api.example.com").method("POST")
             .header("Authorization", "Bearer token").send();

Singleton (thread-safe, C++11)

class Config {
public:
    static Config& instance() {
        static Config inst;  // guaranteed thread-safe since C++11
        return inst;
    }

    Config(const Config&) = delete;
    Config& operator=(const Config&) = delete;

private:
    Config() { /* load config */ }
};

Common mistakes

Mistake Problem Fix
delete raw pointer after move Use-after-free Use smart pointers, never delete manually
Forget virtual destructor in base Derived destructor not called Always add virtual ~Base() = default;
Return local variable reference Dangling reference (UB) Return by value or use static
std::vector invalidation in loop Iterators invalidated by push_back Reserve first or use index loop
Copy in range-for without & Unnecessary copies Use const auto& or auto&
Integer overflow Silent UB Use long long or check before arithmetic
Unguarded shared data Data race (UB) Use mutex or atomic
Exception in destructor std::terminate called Mark destructors noexcept, handle inside

C++ versions quick reference

Version Key features
C++11 auto, range-for, lambdas, move semantics, smart pointers, threads, nullptr, constexpr, enum class
C++14 Generic lambdas, make_unique, digit separators, variable templates
C++17 Structured bindings, if constexpr, std::optional/variant/any, std::filesystem, if/switch initialisers
C++20 Concepts, Ranges, Coroutines, std::span, <format>, three-way comparison, modules
C++23 std::expected, std::generator, std::print, std::flat_map, import std;

C++ vs other languages

Feature C++ Java Python Rust
Memory management Manual + RAII GC GC Ownership/borrow
Compile target Native binary JVM bytecode Interpreted Native binary
Performance Highest High Low-Medium Highest
Zero-cost abstractions Yes No No Yes
Templates/generics Templates Generics (type-erased) Duck typing Generics + traits
Undefined behaviour Yes (many cases) No No Prevented by borrow checker
Use case Systems, games, HPC Enterprise, Android Scripting, ML Systems, safety-critical

FAQ

Q: When should I use C++ instead of Rust or Go?
A: C++ when you need maximum compatibility (decades of libraries), interop with C, mature tooling (MSVC/Clang/GCC), or a team with existing C++ expertise. Rust for new systems code where memory safety matters. Go for services and CLIs where developer speed matters more.

Q: What is undefined behaviour (UB) and why does it matter?
A: UB means the language standard places no constraints on what the compiler does — it can crash, silently produce wrong results, or even appear to work. Common sources: null dereference, signed integer overflow, out-of-bounds access, use-after-free, data races. Use -fsanitize=address,undefined (ASan/UBSan) during development to catch it.

Q: new/delete vs smart pointers — which to use?
A: Smart pointers exclusively in new code. make_unique (sole owner) and make_shared (shared) handle deallocation automatically. Raw new/delete require manual pairing and leak on exceptions. Reserve raw pointers for non-owning references only.

Q: When should I use const&, value, or && for function parameters?
A: Pass by const T& for read-only large objects (strings, containers). Pass by value when the function needs its own copy (then move from it). Pass by T&& (rvalue ref) only when writing constructors/move assignment or perfect-forwarding templates.

Q: How do I avoid include order issues and slow builds?
A: Use forward declarations instead of #include in headers. Prefer #pragma once or proper include guards. For large projects use precompiled headers (PCH) or modules (C++20). Keep implementation in .cpp files, not headers.

Q: What compiler flags should I always use?
A: For development: -Wall -Wextra -Wpedantic -fsanitize=address,undefined -g. For release: -O2 -DNDEBUG (or -O3 for HPC). Enable C++ version explicitly: -std=c++20. Use clang-tidy and clang-format for static analysis and formatting.

Related tools

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools