Appearance
C++ Standards in Practice
C++ evolves in three-year standard cycles, but implementation is incremental. A label such as C++20 identifies a language/library contract, not a promise that every compiler and platform ships every component.
C++98 and C++03 define the legacy language
These editions established templates, exceptions, the original standard containers and algorithms, and the classic object model. Maintained code may use raw owning pointers, iterator-heavy loops, typedef, and preprocessor techniques because later alternatives did not exist.
Recognize the style without reproducing it unnecessarily in new code.
C++11 changed the default design vocabulary
C++11 introduced move semantics, rvalue references, auto, lambdas, range-based for, nullptr, scoped enums, variadic templates, smart pointers, concurrency, constexpr, uniform initialization, and defaulted/deleted functions.
This is the boundary between “classic” and broadly modern C++. The language became much more capable of expressing ownership and generic code directly.
C++14 and C++17 made modern code smoother
C++14 generalized lambdas and relaxed constexpr, among many refinements. C++17 added structured bindings, if constexpr, fold expressions, inline variables, string_view, optional, variant, any, and filesystem support.
These versions are common compatibility baselines for libraries that cannot yet require C++20.
C++20 added concepts, ranges, coroutines, and modules
C++20 is another large revision. Concepts constrain templates; ranges add composable algorithms and views; coroutines provide language machinery for library-defined async/generator abstractions; modules offer an alternative to textual headers; jthread, span, formatting facilities, and many other updates expand the library.
Adoption differs sharply. Concepts and span are broadly straightforward. Modules depend on compiler, build-system, dependency-scanning, and package workflows. Coroutines require an abstraction/library that defines scheduling and ownership; the keyword alone is not an async runtime.
C++23 continues library and language refinement
C++23 adds facilities such as std::expected, std::print, multidimensional subscript improvements, deducing this, more ranges support, and extensive constexpr and library work. Availability is component-specific. Check compiler and standard-library matrices separately.
C++26 work may be described for orientation, but a course should not teach an unreleased or incompletely deployed feature as a production baseline. Revisit this statement as standards and toolchains ship.
Select and detect deliberately
sh
c++ -std=c++20 source.cppMSVC uses /std:c++20; CMake expresses the requirement through target compile features:
cmake
target_compile_features(report PRIVATE cxx_std_20)Feature-test macros such as __cpp_concepts and library macros from <version> support narrow conditional code. Prefer build-time requirements over a forest of fallback branches when all supported targets can upgrade.
Avoid using standard versions as quality labels
A good C++17 design can be safer than careless C++23. New facilities reduce boilerplate and encode intent, but ownership, lifetime, error policy, and tests still decide quality.
Upgrade in two phases: first make the existing code compile and behave under the new mode; then adopt features in reviewable changes. Test warnings and ABI implications across supported compilers.
Optional prompts
Explain: Why was C++11 more than a collection of syntax conveniences?
Answer: Move semantics, RAII-supporting library types, lambdas, concurrency, and stronger language constructs changed how ownership, generic code, and APIs are normally designed.
Explain: Why can modules be harder to adopt than concepts?
Answer: Modules affect dependency discovery, compilation order, build metadata, and package distribution across the whole toolchain, while concepts are primarily a front-end language feature.