Short answer: Compile with g++ -std=c++23 -Wall -Wextra -O2 -o app main.cpp. Prefer std::vector, std::string and std::unordered_map over raw arrays and new; own heap objects with std::unique_ptr. C++20 adds concepts, ranges, std::format and <=>; C++23 adds std::print, std::expected and std::ranges::to. GCC 16 compiles as gnu++20 by default, so pass -std=c++23 to get the newer library features.
The examples in this C++ cheat sheet were run with GCC 14.2 (g++ -std=c++23) and CMake 3.28 on Ubuntu 24.04 on 9 October 2026. Compiler defaults for GCC 16 were checked against the official documentation.
Table of Contents
Compile and build
| Command | What it does |
|---|---|
g++ -std=c++20 -Wall -Wextra -O2 -o app main.cpp | C++20 build with common warnings |
g++ -std=c++23 ... | C++23. std::print needs GCC 14+ (libstdc++) or a recent libc++ |
clang++ -std=c++23 -stdlib=libc++ ... | Clang with the LLVM standard library |
-g -fsanitize=address,undefined | Memory and undefined-behaviour checks in test builds |
-Wconversion -Wshadow | Extra warnings worth enabling on new code |
-pthread | Threads support on older toolchains |
cmake -B build -DCMAKE_BUILD_TYPE=Release | Configure a CMake project into build/ |
cmake --build build -j | Build it in parallel |
cmake_minimum_required(VERSION 3.20)
project(app LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(app main.cpp)
target_compile_options(app PRIVATE -Wall -Wextra)
Variables, types and control flow
| Syntax | What it does | Example |
|---|---|---|
auto | Type deduced from the initialiser | auto n = 42; |
const / constexpr | Read-only / usable at compile time | constexpr int sq(int x) { return x * x; } |
consteval | Function that must run at compile time (C++20) | consteval int cube(int x) |
int& r = n; | Reference: another name for n | void f(const std::string& s); |
{} initialisation | Uniform init; blocks narrowing conversions | std::vector<int> v{1, 2, 3}; |
std::string_view | Non-owning view of a string; cheap to pass | void log(std::string_view msg); |
enum class | Scoped, strongly typed enum | enum class Level { Low, High }; |
nullptr | Null pointer; never use NULL or 0 | int* p = nullptr; |
for (const auto& x : v) | Range-based for loop | |
if (auto it = m.find(k); it != m.end()) | if with an initialiser | |
static_cast<int>(x) | Explicit, checked conversion | static_cast<int>(Level::High) |
1'000'000 | Digit separators |
Containers and common operations
| Container | Use it for | Key operations |
|---|---|---|
std::vector<T> | Default choice: contiguous, growable | push_back, emplace_back, at(i), size(), reserve(n) |
std::array<T, N> | Fixed size known at compile time | a[i], a.size() |
std::string | Owning text | +=, substr, find, starts_with (C++20) |
std::map<K, V> | Sorted keys | m[k] = v, insert_or_assign, contains(k) (C++20) |
std::unordered_map<K, V> | Fast lookup, no order | m[k]++, find, erase |
std::set<T> | Sorted unique values | insert, contains |
std::deque<T> | Fast push and pop at both ends | push_front, pop_back |
std::map<std::string, int> ports{{"ssh", 22}, {"https", 443}};
ports["http"] = 80;
for (const auto& [name, port] : ports) // structured bindings
std::println("{}={}", name, port);
std::vector<int> v{5, 3, 8, 1};
std::erase_if(v, [](int x) { return x % 2 == 0; }); // C++20
m[k] inserts a default value when the key is missing. Use contains or find when you only want to read.
Ranges, algorithms and formatting
| Syntax | What it does |
|---|---|
std::ranges::sort(v); | Sort a whole container, no begin/end pair |
std::ranges::find(v, 42) | Iterator to the first match, or v.end() |
std::ranges::count_if(v, pred) | Count matching elements |
std::ranges::max(v) | Largest value |
v | std::views::filter(pred) | Lazy filtered view |
| std::views::transform(fn) | Lazy mapped view |
std::views::iota(1, 11) | Numbers 1 to 10 |
| std::ranges::to<std::vector>() | C++23: materialise a view into a container |
std::format("{:>8}", s) | C++20 Python-style formatting into a std::string |
std::print / std::println | C++23: format straight to stdout |
auto squares = std::views::iota(1, 11)
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; })
| std::ranges::to<std::vector>(); // 4 16 36 64 100
std::println("{:>8}|{:<6}|{:08.3f}|{:#x}", "right", "left", 3.14159, 255);
// output: right|left |0003.142|0xff
Classes, RAII and smart pointers
class Shape {
public:
virtual ~Shape() = default;
virtual double area() const = 0;
};
class Square final : public Shape {
public:
explicit Square(double side) : side_{side} {}
double area() const override { return side_ * side_; }
private:
double side_;
};
struct Server {
std::string name;
int port = 22;
auto operator<=>(const Server&) const = default; // C++20: all comparisons
};
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Square>(3.0));
| Syntax | Meaning |
|---|---|
std::unique_ptr<T> + std::make_unique<T>(...) | Single owner, freed automatically; move-only |
std::shared_ptr<T> + std::make_shared<T>(...) | Shared ownership with a reference count |
override / final | Compiler checks the virtual function really overrides |
= default / = delete | Ask for or forbid a generated member (e.g. copying) |
explicit | No implicit conversions through this constructor |
| Rule of zero | Use members that manage themselves; write no destructor or copy code |
RAII means a resource is released in a destructor, so files, locks and sockets close even when an exception is thrown. Avoid raw new and delete in application code.
Templates and concepts
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;
template <Number T>
T add(T a, T b) { return a + b; }
auto twice(std::integral auto x) { return x * 2; } // abbreviated template
add(2, 3); // 5
add(1.5, 2.0); // 3.5
// add("a", "b"); // error: constraints not satisfied
Concepts from <concepts> give short, readable errors when a type does not fit, instead of pages of template noise.
Errors: exceptions, optional and expected
| Tool | Use it when |
|---|---|
throw / try / catch (const std::exception& e) | Unexpected failures that callers far up the stack should handle |
std::optional<T> | A value may be absent; read with if (opt), *opt, value_or() |
std::expected<T, E> (C++23) | Success or a typed error; read with if (r), *r, r.error() |
v.at(i) | Bounds-checked access; throws std::out_of_range |
noexcept | Marks a function that never throws |
std::expected<int, std::string> parse_port(std::string_view s) {
int v = 0;
for (char c : s) {
if (c < '0' || c > '9')
return std::unexpected(std::format("not a number: {}", s));
v = v * 10 + (c - '0');
}
if (v < 1 || v > 65535) return std::unexpected("out of range");
return v;
}
if (auto r = parse_port("70000"); r) std::println("port {}", *r);
else std::println("error: {}", r.error()); // error: out of range
Files, threads and a log example
For server admins: count requests per client IP in an Nginx or Apache access log.
#include <algorithm>
#include <fstream>
#include <print>
#include <ranges>
#include <string>
#include <unordered_map>
#include <vector>
int main(int argc, char* argv[]) {
if (argc != 2) { std::println(stderr, "usage: {} access.log", argv[0]); return 1; }
std::ifstream in{argv[1]};
if (!in) { std::println(stderr, "cannot open {}", argv[1]); return 1; }
std::unordered_map<std::string, int> hits;
for (std::string line; std::getline(in, line); )
++hits[line.substr(0, line.find(' '))];
std::vector<std::pair<std::string, int>> top(hits.begin(), hits.end());
std::ranges::sort(top, [](const auto& a, const auto& b) { return a.second > b.second; });
for (const auto& [ip, n] : top | std::views::take(10))
std::println("{:>7} {}", n, ip);
}
| Syntax | What it does |
|---|---|
std::filesystem::exists(p) | Check a path (namespace fs = std::filesystem;) |
fs::directory_iterator(dir) | List a directory; recursive_directory_iterator walks it |
std::jthread t{fn}; | C++20 thread that joins itself on destruction |
std::scoped_lock lock{m}; | Lock one or more mutexes until the end of scope |
std::atomic<int> n{0}; n.fetch_add(1); | Lock-free counter |
std::async(std::launch::async, fn) | Run a task and get a std::future |
Data races are undefined behaviour. Protect shared data with a mutex or an atomic, and build test binaries with -fsanitize=thread to find races.
Official documentation (checked 9 October 2026): C++ Standards Support in GCC · libstdc++ implementation status · GCC 16 Changes (C++20 by default) · Clang C++ Language Status · WG21 N4950 (C++23 working draft) · C++ Core Guidelines · CMake: CXX_STANDARD
C++ Cheat Sheet at a glance


Related guides: C Cheat Sheet: C17 and C23 Syntax, Memory and gcc Flags · Rust Cheat Sheet: Cargo, Ownership, Enums and Errors · C# Cheat Sheet: Syntax, LINQ and .NET 10 Essentials.
Reading a lot of our guides? Go ad-free with srvScripts Pro for $6 a month. It also pays for new guides. Cancel any time.