C++, End to End
A Tour of Modern C++ (11 Through 23)
C++ has changed more in the last fifteen years than in the twenty before that. This chapter is a survey: what each standard actually added, at a practical level. Several of these (auto, lambdas, smart pointers, move semantics) already got full chapters earlier in this course and are just listed here for context, not re-explained.
C++11: the big one
- auto and range-based for: type deduction and simpler iteration (covered earlier)
- Move semantics, rvalue references, smart pointers (covered earlier, this is arguably the single biggest change to how idiomatic C++ is written)
- Lambdas (covered earlier)
- nullptr replacing the old NULL macro (which was really just 0 in disguise, causing real overload-resolution bugs)
- Scoped enums, override/final (covered earlier)
- Uniform initialization with braces {}
C++14: a smaller, cleanup release
- Generic lambdas: auto in a lambda's parameter list, letting one lambda work across multiple types
- Relaxed constexpr rules: constexpr functions could now contain loops and multiple statements, not just a single return expression
C++17: quality of life
- Structured bindings: unpack a pair/tuple/struct into named variables in one line
- if with an initializer: if (auto it = m.find(key); it != m.end())
- std::optional, std::variant, std::any: safer alternatives to nullable pointers and unsafe unions
- Fold expressions for variadic templates (covered in the templates chapter)
C++20: a second C++11
- Concepts (covered in the templates chapter): named, readable template constraints
- Ranges: composable operations over sequences, e.g. std::views::filter and std::views::transform, without hand-writing loops or intermediate containers
- The spaceship operator <=>: write one comparison and get all six relational operators generated for you
- Coroutines: language support for suspendable functions (co_await, co_yield). Powerful, but the library-level ergonomics (writing your own coroutine types) are still genuinely fiddly; most people use coroutines through a library that wraps the hard parts rather than building the machinery by hand
- Modules: an alternative to the #include/header-file model, meant to speed up compilation and avoid macro leakage. Compiler and build-system support is still catching up as of this writing, so don't be surprised if module support varies noticeably between toolchains
That ranges pipeline reads left to right (filter, then transform) instead of nesting function calls inside out, and importantly, it's lazy: nothing is actually computed until you iterate over the result.
C++23: still settling in
- std::expected<T, E>: a return type that's either a value or an error, without throwing, a genuine standard-library alternative to exceptions for error handling
- "Deducing this": lets a member function deduce its own object parameter, simplifying some patterns that previously needed duplicated const/non-const overloads
- std::print / std::println: type-safe, format-string-based printing (from C++20's std::format, extended in C++23), meant to eventually be the default way you write to stdout instead of iostream chaining
Compiler and standard library support for C++23 features is genuinely uneven as of this writing. Treat the C++23 examples in this course as illustrative of the direction the language is heading, and double-check your specific toolchain's support before relying on them in real code.
Try it yourself: rewrite a loop as a ranges pipeline›
Take a plain for loop that filters and transforms a vector into a new vector, and rewrite it using std::views::filter and std::views::transform, then compare readability.