Cheat Sheet Library

C++ Cheat Sheet

Quick reference for modern C++: compiling, strings, STL containers and algorithms, lambdas, classes, smart pointers, templates and concepts. Targets C++20, with C++23 features marked "(C++23)".

  • 124 entries
  • 14 sections
  • Updated

124 entries

On this page

Compile & Run #

CommandWhat it does
g++ -std=c++20 -Wall -Wextra main.cpp -o appCompile with warnings as C++20 (clang++ takes the same flags)
./appRun it (Windows: app.exe)
-std=c++23Use C++23 (older compilers: -std=c++2b)
-g / -O2Debug info / optimize for speed
g++ main.cpp util.cpp -o appCompile and link several files
-fsanitize=address,undefinedCatch memory errors and undefined behaviour at run time

Basics & Output #

CodeWhat it does
#include <iostream>Standard input/output streams
std::cout << "x = " << x << '\n';Print (prefer '\n' over std::endl, which also flushes)
std::cin >> n;Read a value (stops at whitespace)
std::getline(std::cin, line);Read a whole line into a std::string
std::cerr << "error\n";Print to the error stream
std::format("{} + {} = {}", a, b, a + b)Python-style formatting, returns a std::string (C++20, <format>)
std::format("{:.2f}", 3.14159)"3.14": 2 decimals
std::format("{:>6}|{:<6}|{:^6}", a, b, c)Right / left / centre align in 6 characters
std::format("{:05} {:x} {:#b}", 42, 255, 5)"00042 ff 0b101": zero-pad, hex, binary with prefix
std::print("Hi {}\n", name);Format and print directly (C++23, <print>; GCC 14+, recent Clang/MSVC)
std::println("x = {}", x);Same, adds a newline (C++23)

References, const & auto #

CodeWhat it does
int& r = x;Reference: another name for x, must be initialized, can't be reseated or null
int* p = &x;Pointer: can be nullptr and can point elsewhere later
void inc(int& x)Pass by reference: the function changes the caller's variable
void show(const std::string& s)Pass big objects by const& to avoid a copy
const int max = 10;Can't be changed after initialization
constexpr int size = 16;Compile-time constant
constexpr int square(int x) { return x * x; }Can be evaluated at compile time
auto it = m.find(key);Let the compiler deduce the type
for (const auto& item : items)Range-based for loop without copying
for (auto& item : items)Loop and modify the elements in place
nullptrNull pointer (don't use NULL or 0 in C++)

std::string & std::string_view #

CodeWhat it does
std::string s = "hello";Owning, growable string (<string>)
s += " world"; / s + tAppend / concatenate
s.size() / s.empty()Length / is it empty
s.substr(pos, len)Part of the string (a new copy)
s.find("lo")Position of the match, or std::string::npos
s.starts_with("he") / s.ends_with("lo")Prefix / suffix check (C++20)
s.contains("ell")Substring check (C++23)
std::to_string(42)Number to string
std::stoi(s) / std::stod(s)String to int / double (throws on bad input)
void greet(std::string_view name)Read-only view of any string, no copy (C++17)
std::string(sv)Make an owning copy from a string_view

Containers #

CodeWhat it does
std::vector<int> v{1, 2, 3};Dynamic array, the default container
v.push_back(4); / v.emplace_back(args)Add to the end / construct in place
v.pop_back(); / v.clear();Remove the last element / all elements
v[i] / v.at(i)Element access: unchecked / bounds-checked (throws std::out_of_range)
v.front() / v.back() / v.size()First / last element / count
v.insert(v.begin() + i, x) / v.erase(v.begin() + i)Insert / remove at position i
std::erase_if(v, pred)Remove all matching elements (C++20)
std::array<int, 3> a{};Fixed-size array that knows its size
std::deque<int> d;Fast push_front / push_back at both ends
std::map<std::string, int> m;Sorted key-value pairs
m["Ann"] = 30;Insert or update (m[key] creates the key if missing)
m.contains("Ann")Key exists? (C++20; before: m.count(k) or m.find(k) != m.end())
m.erase("Ann");Remove a key
std::unordered_map<std::string, int> h;Hash map: faster lookups, no order
std::set<int> s{3, 1, 3};Sorted unique values: {1, 3} (unordered_set = no order)

Algorithms & Ranges #

CodeWhat it does
std::sort(v.begin(), v.end());Sort ascending (<algorithm>)
std::sort(v.begin(), v.end(), std::greater<>());Sort descending
std::ranges::sort(v);Same, no iterator pair needed (C++20)
std::find(v.begin(), v.end(), x)Iterator to the first match, or v.end()
std::count_if(v.begin(), v.end(), pred)Count elements matching a predicate
std::transform(v.begin(), v.end(), out.begin(), f)Apply f to each element into out
std::accumulate(v.begin(), v.end(), 0)Sum (<numeric>; start value sets the type)
std::max_element(v.begin(), v.end())Iterator to the largest element
std::ranges::find(v, x) / std::ranges::count_if(v, pred)Range versions of the classics (C++20)
std::ranges::contains(v, x)Does v contain x? (C++23)
v | std::views::filter(pred) | std::views::transform(f)Lazy pipeline over a range (C++20)
std::views::iota(1, 11)The numbers 1 to 10, lazily

Lambdas #

CodeWhat it does
[](int x) { return x * 2; }Lambda with no captures
[x]Capture x by value (a copy)
[&total]Capture total by reference (can change it)
[=] / [&]Capture everything used by value / by reference
[this]Capture the current object inside a member function
[c = 0]() mutable { return ++c; }Init-capture; mutable lets the copy change
[](auto a, auto b) { return a + b; }Generic lambda, works for any types
auto cmp = [](const P& a, const P& b) { return a.age < b.age; };Store a lambda in a variable, e.g. for std::sort

Classes #

class Account {
public:
    explicit Account(std::string owner, double balance = 0)
        : owner_{std::move(owner)}, balance_{balance} {}   // member initializer list
    double balance() const { return balance_; }            // const: doesn't modify
    void deposit(double amount);
private:
    std::string owner_;
    double balance_;
};
CodeWhat it does
explicit Account(...)Constructor that can't be used for implicit conversions
Account() = default;Ask for the compiler-generated version
Account(const Account&) = delete;Forbid copying
virtual double area() const = 0;Pure virtual: makes the class abstract
virtual ~Shape() = default;Base classes used polymorphically need a virtual destructor
double area() const overrideOverrides a base virtual; compiler checks the signature
class Circle final : public Shapefinal: no class can inherit from it
Vec2 operator+(const Vec2& o) constOperator overloading
bool operator==(const Vec2&) const = default;Generated equality (C++20; <=> for all comparisons)
friend std::ostream& operator<<(std::ostream&, const Vec2&)Make std::cout << v work
Rule of 0Use std::string, std::vector, smart pointers so you need no destructor or copy/move code
Rule of 3 / 5Write a destructor? Then also copy constructor + copy assignment (+ move constructor + move assignment)

Smart Pointers & Move Semantics #

CodeWhat it does
auto p = std::make_unique<Circle>(2.0);Sole owner; deleted automatically (<memory>)
std::unique_ptr<Shape> s = std::move(p);Transfer ownership; p is now nullptr
auto sp = std::make_shared<Account>("Ann");Shared ownership with a reference count
sp.use_count()How many shared_ptrs own the object
std::weak_ptr<Node> parent;Non-owning link to a shared_ptr (breaks cycles)
p.get() / p.reset()Raw pointer without giving up ownership / delete now
std::vector<std::unique_ptr<Shape>>Container of polymorphic objects
std::string b = std::move(a);Steal a's contents instead of copying; a is left valid but unspecified
Buffer(Buffer&& other) noexceptMove constructor; mark it noexcept so vector uses it
void add(std::string name) + std::move(name)Take by value, then move into place: cheap for temporaries

Templates & Concepts #

template <typename T>
T maxOf(T a, T b) { return a > b ? a : b; }

template <typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;   // C++20, <concepts>

template <Numeric T>
T sum(const std::vector<T>& v) { T s{}; for (const auto& x : v) s += x; return s; }

auto half(std::floating_point auto x) { return x / 2; }          // abbreviated template (C++20)
CodeWhat it does
maxOf<double>(2, 2.5)Specify the template argument explicitly
template <typename T, std::size_t N>Template with a value parameter (like std::array)
requires std::totally_ordered<T>Constrain a template with a requires clause (C++20)
std::integral / std::floating_point / std::same_as<T, U>Common standard concepts

Exceptions #

CodeWhat it does
throw std::invalid_argument("amount must be positive");Throw an exception (<stdexcept>)
try { ... } catch (const std::exception& e) { e.what(); }Catch by const& and read the message
catch (const std::out_of_range& e)Catch a specific type; put specific catches before general ones
catch (...)Catch anything
throw;Re-throw the current exception inside a catch
void f() noexceptPromise not to throw (destructors and moves should be)

Structured Bindings, optional & variant #

CodeWhat it does
auto [q, r] = std::pair{17 / 5, 17 % 5};Unpack a pair, tuple or struct (C++17)
for (const auto& [key, value] : m)Loop over a map with named parts
if (auto it = m.find(k); it != m.end())if with an initializer (C++17)
std::optional<int> parse(const std::string& s)Maybe a value; return std::nullopt for "none" (<optional>)
if (auto v = parse(s)) use(*v);Check and use an optional
opt.value_or(-1)The value, or a default
std::variant<int, std::string> v = "hi";Holds one of several types (<variant>)
std::holds_alternative<int>(v) / std::get<int>(v)Check the active type / read it (throws if wrong)
std::visit([](const auto& x) { ... }, v);Run code for whichever type is active

Common Pitfalls #

MistakeWhat goes wrong
std::vector<int> v{3}; vs v(3){3} is one element with value 3; (3) is three zeros
for (int i = 0; i < v.size(); i++)Signed/unsigned comparison; use range-for or std::size_t
v.push_back(x) while iterating vInvalidates iterators and references into the vector
std::string_view sv = std::string("tmp");Dangling view: the temporary string is already destroyed
Missing virtual destructorDeleting a derived object through a base pointer leaks or crashes
Base b = derived;Object slicing: the derived part is cut off; use references or pointers
m[key] just to check a keyInserts an empty entry; use contains or find
new / delete by handEasy to leak; use std::make_unique or containers
Using an object after std::moveIt's in a valid but unspecified state; assign a new value first

Tips #

  • Compile with -Wall -Wextra and during development with -g -fsanitize=address,undefined.
  • Default to std::vector, std::string and std::unique_ptr; reach for other containers or shared_ptr only when you need their features.
  • Pass small types (int, double, string_view) by value and big objects by const&.
  • Prefer algorithms and range-for over hand-written index loops: fewer off-by-one bugs.
  • Mark single-argument constructors explicit and overriding functions override; the compiler then catches mistakes for you.