Modern C++ is a collection of deep, code-first documentation for the C++ language as it exists today — C++11 through C++23. Every page is built around complete, working programs. Samples marked with a Run in Compiler Explorer link open directly in Compiler Explorer with the right flags already set, so you can run and modify them in one click. Everything on this site compiles cleanly with -std=c++23 -Wall -Wextra.
Features are labeled with the standard that introduced them, like this: C++23. If a page covers a feature that changed across standards, each refinement is labeled where it appears.
Core language features
How to write today's C++ at the language level: type deduction, initialization, enumerations, iteration, conversions, namespaces, and the deduction machinery that removes boilerplate.
- Using auto whenever possible
Deduce types instead of spelling them: locals, qualifiers, return types, generic lambdas — and the cases where auto surprises you.
- Creating type aliases and alias templates
The using declaration as the full replacement for typedef, and alias templates for parameterized names.
- Understanding uniform initialization
Brace initialization for every kind of object, narrowing protection, the initializer_list trap, and designated initializers.
- Non-static member initialization
Default member initializers, constructor initializer lists, initialization order, and which form to use when.
- Controlling and querying object alignment
alignas and alignof, why alignment exists, over-aligned types, and cache-line-aware layout.
- Using scoped enumerations
enum class: real scoping, no implicit conversions, chosen underlying types, using enum, and std::to_underlying.
- Virtual methods with override and final
Making the compiler verify your overrides, sealing hierarchies, and the bugs these two words eliminate.
- Iterating with range-based for loops
What the loop actually expands to, choosing the right element binding, init-statements, and C++23's temporary-lifetime fix.
- Enabling range-based for on your own types
The exact protocol the compiler looks for, writing a minimal iterator, and sentinel-terminated ranges.
- Avoiding implicit conversion with explicit
Converting constructors, conversion operators, the bugs implicit conversions cause, and conditional explicit(bool).
- Unnamed namespaces instead of static globals
Internal linkage done right: per-file helpers, ODR safety, and why static at namespace scope is the weaker tool.
- Inline namespaces and symbol versioning
Publishing versioned APIs under one name, how the standard library uses them, and ABI-safe evolution.
- Structured bindings and multiple return values
Decomposing pairs, tuples, structs, and arrays; returning multiple values without out-parameters.
- Class template argument deduction
Letting the compiler deduce class template arguments, writing your own deduction guides, and knowing when to opt out.
- The subscript operator, from operator[] to C++23
Writing correct subscript access for your own collections, const and non-const pairs, multidimensional operator[], and deducing this.
Numbers and strings
Working with data's two most common shapes: numeric types and their properties, text in all its encodings, randomness done right, user-defined literals, regular expressions, and the modern formatting stack.
- Understanding the various numeric types
Fundamental integers and floats, fixed-width aliases, overflow and underflow at the edges, mixed-sign traps, and modern literal syntax.
- Limits and other properties of numeric types
std::numeric_limits: min, max, lowest, epsilon, precision digits — and putting each to work correctly.
- Understanding the various character and string types
char through char32_t, five literal encodings, code units vs characters, and which string to actually use.
- Printing Unicode characters to the console
Getting UTF-8 from source to terminal intact, platform setup, escapes, and normalization surprises.
- Generating pseudo-random numbers
The engine-plus-distribution design of <random>, choosing each, and why rand() is never the answer.
- Properly initializing a pseudo-random number generator
random_device, seed_seq, full-state seeding, and treating seeds as reproducibility data.
- Creating cooked user-defined literals
Literal operators that attach units to values — 64_KiB, 90.0_deg — with compile-time validation.
- Creating raw user-defined literals
Operators that see the literal's original spelling: exact decimals, other bases, per-digit validation.
- Using raw string literals to avoid escaping characters
R"(...)" syntax, custom delimiters, multi-line text, and why every regex belongs in one.
- Creating a library of string helpers
trim, case mapping, split, join, replace_all — the missing std::string utilities, built correctly once.
- Parsing the content of a string using regular expressions
regex_match vs regex_search, capture groups, iterating matches, and honest performance guidance.
- Replacing content of a string using regular expressions
regex_replace, backreferences, format flags, and the callback pattern the standard forgot.
- Using std::string_view instead of constant string references
The non-owning parameter type, allocation-free parsing, and the lifetime rules that keep it safe.
- Formatting and printing text with std::format and std::print
The {} mini-language, compile-time checked format strings, and C++23's print family.
- Using std::format with user-defined types
Specializing std::formatter: the delegation shortcut, custom specs, and range formatting.
Exploring functions
Everything callable: explicit control of the special member functions, lambdas from first principles through recursion, templates over any number of arguments, fold expressions, and the higher-order patterns — map, fold, composition, uniform invocation — that turn functions into building blocks.
- Defaulted and deleted functions
= default and = delete: restoring suppressed special members, preserving triviality, non-copyable types, and rejecting the wrong overloads.
- Using lambdas with standard algorithms
Capture semantics, init captures for move-only state, and the lambda patterns that make the algorithm library click — including C++20 projections.
- Using generic and template lambdas
auto parameters as invisible templates, C++20 template heads on lambdas, constraining parameters with concepts, and forwarding inside a closure.
- Writing a recursive lambda
Why a lambda can't name itself, the std::function and self-passing workarounds, and C++23's deducing this that solves it cleanly.
- Writing function templates
Deduction and what it does to your arguments, non-type parameters, overloading versus specialization, and constraining with concepts.
- Writing a function template with a variable number of arguments
Parameter packs, sizeof..., the recursive expansion pattern, if constexpr base cases, and perfect forwarding through a pack.
- Using fold expressions to simplify variadic function templates
All four fold forms and their exact expansions, the 32 supported operators, empty-pack rules, and folds that replace whole overload sets.
- Implementing the higher-order functions map and fold
Building map and fold generically with invoke_result and inserters, then their standard names: transform, accumulate, and C++23's fold_left.
- Composing functions into a higher-order function
A variadic compose() from lambdas, pipeline direction, bind_front and bind_back, and how range adaptors made composition a core idiom.
- Uniformly invoking anything callable
std::invoke's unified call rules for functions, members, and functors; member pointers as projections; invoke_r; and the invocation traits.
Preprocessing and compilation
What happens before and during compilation, and how to steer it: conditional compilation, the preprocessor's token machinery, assertions the compiler evaluates, templates that filter themselves out of overload resolution, branches selected at compile time, and the metadata attributes that make the compiler catch your callers' mistakes.
- Conditionally compiling your source code
The #if family, platform and compiler detection, NDEBUG, __has_include, feature-test macros, and C++23's #elifdef.
- Using the indirection pattern for stringification and concatenation
Why # and ## refuse to expand their arguments, the two-level fix, version strings, unique names, and assertion messages.
- Performing compile-time assertion checks with static_assert
Layout and ABI guards, template preconditions, the dependent-false idiom, and choosing between static_assert, assert, and concepts.
- Conditionally compiling classes and functions with enable_if
SFINAE from first principles, the placement idioms and the redefinition trap, constraining class templates, and what replaced it all.
- Selecting branches at compile time with constexpr if
The discard rule and its limits, recursion without base cases, different return types per branch, and C++23's if consteval.
- Providing metadata to the compiler with attributes
Every standard attribute from [[noreturn]] to C++23's [[assume]] - what each is for and how to use it honestly.
Standard library containers, algorithms, and iterators
The standard library's working core: the containers that own your data, the algorithms that transform it, and the iterators that connect the two. This chapter is in progress — pages land as they are finished.
- Using vector as a default container
Why contiguous storage wins by default: every way to create one, size vs capacity, adding and removing elements, the invalidation rules, and lending the buffer to C APIs.
- Using bitset for fixed-size sequences of bits
Exactly N bits with named operations: building bitsets from integers and strings, testing and flipping bits, the bitwise operators, conversions out, and replacing hand-rolled flag masks.
- Using vector<bool> for variable-size sequences of bits
The packed vector specialization as a run-time-sized bitset: what the proxy reference changes, spelling bitset's verbs with algorithms, the sieve sized at run time, and when real bools serve better.
- Using the bit manipulation utilities
The C++20 <bit> header's integer utilities: counting and visiting set bits, the power-of-two helpers, rotations without undefined edges, endian and byteswap, and type punning with bit_cast.
- Finding elements in a range
The standard library's search algorithms: find and its predicate variants, locating subranges from either end, searchers for fast substring scans, the min and max element family, and the logarithmic lookups on sorted ranges.
- Sorting a range
The standard sorting algorithms: sort and its stability-preserving stable_sort, partial sorts in place or into a separate copy, nth_element for selection without a full sort, and the is_sorted checks that report whether and how far a range is ordered.
- Initializing a range
The algorithms that fill a range with values: fill and fill_n for a single value, generate and generate_n for values from a function, iota for consecutive sequences, and a real-life color gradient that puts them to work.
- Using set operations on a range
The algorithms that combine sorted ranges: set_union and merge for putting two ranges together, set_intersection for what they share, set_difference and set_symmetric_difference for what they do not, includes for subset tests, and a task type that reveals how each one chooses among equivalent elements.
- Using iterators to insert new elements in a container
The adapters that let an algorithm grow a container: back_inserter, front_inserter, and inserter, the output-iterator operations they redefine to make assignment mean insertion, the reversal front_inserter performs, and the position hint that associative containers are free to ignore.
- Writing your own random-access iterator
In progress.
In progress
Phases 1 through 4 cover core language features, working with numbers and strings, exploring functions, and preprocessing and compilation; phase 5 — the standard library's containers, algorithms, and iterators — is underway above. Future phases will go equally deep on ranges, general-purpose utilities, and threading and concurrency.
External references
- cppreference.com
The community reference for the C++ language and standard library.
- Compiler support tables
Which compiler versions implement each C++20/C++23 feature.
- Working draft of the C++ standard
The language, straight from the source.