Emergency server help: get in touch

C++ Cheat Sheet: C++20 and C++23 Syntax, STL and Ranges

A compact C++20 and C++23 reference covering compiling, the STL, ranges, formatting, classes, concepts, error handling and threads.

Published 7 min read

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.

Compile and build

CommandWhat it does
g++ -std=c++20 -Wall -Wextra -O2 -o app main.cppC++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,undefinedMemory and undefined-behaviour checks in test builds
-Wconversion -WshadowExtra warnings worth enabling on new code
-pthreadThreads support on older toolchains
cmake -B build -DCMAKE_BUILD_TYPE=ReleaseConfigure a CMake project into build/
cmake --build build -jBuild 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

SyntaxWhat it doesExample
autoType deduced from the initialiserauto n = 42;
const / constexprRead-only / usable at compile timeconstexpr int sq(int x) { return x * x; }
constevalFunction that must run at compile time (C++20)consteval int cube(int x)
int& r = n;Reference: another name for nvoid f(const std::string& s);
{} initialisationUniform init; blocks narrowing conversionsstd::vector<int> v{1, 2, 3};
std::string_viewNon-owning view of a string; cheap to passvoid log(std::string_view msg);
enum classScoped, strongly typed enumenum class Level { Low, High };
nullptrNull pointer; never use NULL or 0int* 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 conversionstatic_cast<int>(Level::High)
1'000'000Digit separators

Containers and common operations

ContainerUse it forKey operations
std::vector<T>Default choice: contiguous, growablepush_back, emplace_back, at(i), size(), reserve(n)
std::array<T, N>Fixed size known at compile timea[i], a.size()
std::stringOwning text+=, substr, find, starts_with (C++20)
std::map<K, V>Sorted keysm[k] = v, insert_or_assign, contains(k) (C++20)
std::unordered_map<K, V>Fast lookup, no orderm[k]++, find, erase
std::set<T>Sorted unique valuesinsert, contains
std::deque<T>Fast push and pop at both endspush_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

SyntaxWhat 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::printlnC++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));
SyntaxMeaning
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 / finalCompiler checks the virtual function really overrides
= default / = deleteAsk for or forbid a generated member (e.g. copying)
explicitNo implicit conversions through this constructor
Rule of zeroUse 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

ToolUse 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
noexceptMarks 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);
}
SyntaxWhat 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

C++ Cheat Sheet – short answer
In short: A compact C++20 and C++23 reference covering compiling, the STL, ranges, formatting, classes, concepts, error handling and threads.
C++ Cheat Sheet – what this guide covers
C++ Cheat Sheet: what this guide covers.

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.

Share this guide

Free website test

Is your website set up right?

Check SSL, security headers, redirects, robots.txt, sitemap, llms.txt and security.txt in one test. It takes about 30 seconds.