Wiki
ModernC++
Existing English translations were reused. 6 current source blocks remain in Simplified Chinese, without triggering paid translation.
"Modern C++" generally refers to a series of syntax features and library tools introduced by the C++11 standard and later versions. It does not completely overturn all old C++, but rather builds on the existing syntax to provide safer, clearer, and more engineering-oriented ways of expression.
This chapter is organized in the order of "from shallow to deep, from commonly used to advanced": first learn the syntax used every day, then learn lifecycle and resource management, followed by callbacks, data representation, time, files, and concurrency, and finally understand modular compilation.
1.Conventions for Examples in This Chapter
To make it easy for you to copy and verify directly, the "example code" in each section of this chapter follows these conventions:
- The example code includes complete
#include. - The example code includes the
mainfunction. - The example code can be copied into a single
.cppfile for compilation and execution. - The running result is given below each example code.
- Examples try to introduce only one new concept at a time, avoiding cramming too many things into one example.
The "common mistakes" in the text are used to explain what not to write. To avoid accidental copying, incorrect forms will be placed as much as possible in textual explanations or short fragments, and not treated as runnable examples.
2.两种系统中的编译标准
Linux 和 Windows 继续使用前面配置好的 CMake 工程,分别选择 linux-debug 和 windows-debug。语法和标准库示例不需要因为操作系统不同复制两份。
注意,本模板默认 C++20,后面部分例程使用 C++23 的 <print>、std::print() 和 std::println()。运行这些例程前,需要同时满足两件事:
- 当前 target 使用 C++23,例如在它的 CMake 配置中添加
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_23)。 - 实际编译器和标准库已经实现相应功能。只改
-std=c++23不会给旧标准库补上<print>。
例如 Ubuntu 22.04 的系统 GCC 11 可以运行入门模板,但不能据此认为它支持后面所有 C++23 例程。GNU 标准库的 std::format 从 GCC 13 系列提供,格式化输出 <print> 从 GCC 14 系列提供;完整能力和链接要求以 GCC 标准库支持表 为准。
Windows 用 g++ --version 确认当前 UCRT64 工具链版本,Linux 也检查实际执行的编译器。遇到不支持的特性时,应升级相应工具链,或先使用前面已经学过的输出方式练习其他知识点。
3.Learning Sequence
| Stage | Problems to solve first | Corresponding chapters |
|---|---|---|
| Basic expressions | Write fewer duplicate types, reduce null pointers and enum mixing, and make iteration clearer | auto、nullptr、using、enum class、range for, structured bindings |
| Lifetime | When are resources released, who owns objects, and how are large objects passed efficiently | constexpr、RAII, smart pointers, rvalue references and move semantics |
| Callbacks and data models | Small functions, callback storage, parameter adaptation, possibly no value, one of multiple types | Lambda,std::function、std::bind、std::optional、std::variant、std::span |
| System capabilities | Formatted output, time, concurrency, filesystem, modularization | std::format / std::print、std::chrono、concurrent programming,std::filesystem、modules |
The focus of modern C++ is not "grammar updates," but rather "writing the intent clearly." For example:
nullptrLet null pointers and integers0be separated.enum classPrevent different enumeration types from being mixed arbitrarily.- RAII and smart pointers delegate resource release to object lifetime.
- Lambda lets local callbacks be written at the point of use.
std::functionStore and pass callable objects of different types in a unified way.std::format/std::printMake formatted output more concise while maintaining type safety.
4.Relationship between old and modern orthography
Many modern C++ features come from a simple problem: the old way works, but it is prone to errors in complex scenarios.
| old style | Main issues | modern style |
|---|---|---|
NULL / 0 indicates null pointer |
confused with integer overloads | nullptr |
typedef write type aliases |
template aliases are not intuitive | using |
| plain enum | enum names pollute the scope and can be implicitly converted to integers | enum class |
manual new/delete |
early return, exceptions, and double free are all error-prone | RAII, std::unique_ptr , std::shared_ptr |
manual lock/unlock |
forgetting to unlock causes deadlock, and exception paths are even more dangerous | std::lock_guard , std::unique_lock |
| defining an ordinary function far away for a simple callback | local logic gets scattered and cannot conveniently carry context | Lambda |
| using only function pointers to store callbacks | cannot store lambdas with captures and function objects | std::function |
| hand-writing adapter functions to adjust parameters | code duplication; adapting old interfaces is troublesome | std::bind or Lambda |
| using special values to indicate failure | -1magic values such as empty strings have unclear semantics |
std::optional |
use union to represent multiple types |
accessing the wrong type causes undefined behavior | std::variant |
| manually writing time unit conversions | seconds, milliseconds, and microseconds are easy to mix up | std::chrono |
| platform-dependent file APIs | Windows/Linux are written differently | std::filesystem |
When learning each feature, you can understand it through three questions:
- Where is the old syntax prone to errors?
- How does the modern syntax encode intent into types or grammar?
- In what scenarios does the difference become evident?
For example, if a smart pointer example only creates an object in main and then ends normally, it looks roughly the same as manual delete; once early return, exceptions, or cross-function passing occur, the value of RAII immediately becomes apparent. If a lambda is only called immediately, capture by value and capture by reference may both be fine; once it is stored in a callback, thread, or timer, the lifetime differences become very important.
5.Recommended Learning Path
5.1.Make the code clearer
First learn auto、nullptr、using、enum class、range for, and structured bindings. These topics are not very difficult, but they can significantly reduce verbose code and low-level errors.
At this stage, focus on remembering:
autois not weak typing; it just lets the compiler write types for you.nullptrrepresents only a null pointer, not an integer.enum classis not mixed with integers by default.- The range for uses
const auto&to iterate over large objects by default. - Structured bindings are suitable for unpacking
pair,tuple, structs, andmapelements.
5.2.Understanding the Lifecycle
Next, learn about constexpr, RAII, smart pointers, and move semantics. At this stage, more important than syntax is the way of thinking: when an object is created, when it is destroyed, who owns it, and who merely borrows it.
It is recommended to understand it in this order:
- RAII: Bind resource and object lifetimes.
unique_ptr: Exclusive ownership, the default choice.shared_ptr: Use only when multiple owners are genuinely needed.weak_ptr: Observe only, do not extend lifetime, used to break reference cycles.- Move semantics: Transfer resources out to avoid deep copies of large objects.
5.3.Master callbacks and data representation
Lambda,std::function、std::bind The three chapters should be studied together, but do not memorize them mixed together:
| Tools | Problem solved | Key points |
|---|---|---|
| Lambda | Write a small function at the point of use | Capture list, parameters, return value, lifetime |
std::function |
Uniformly store and pass callable objects of different types | Type erasure, callback member variables, callback containers |
std::bind |
Adapt the parameters of an existing function | Fixed parameters, adjust order, bind member functions |
std::optional、std::variant、std::span respectively solve the problems of "possibly having no value", "one of several types", and "borrowing a contiguous segment of data".
5.4.Towards Engineering Capability
Finally, learn formatted output, time, concurrency, the file system, and modules. This content is closer to real projects:
std::format/std::printthanstringstreamConcise, thanprintfType-safe.std::chronoAvoid handwritten time unit conversions.- Concurrent programming must first ensure correctness, then consider speed.
std::filesystemMake path, directory, and file information handling cross-platform.- modules is a new compilation model introduced in C++20. At present, you should first understand the concepts, then decide whether to use it based on toolchain support.
6.Practical Exercise Suggestions
- Learn
nullptr: Write two overloaded functions, receivingintandint*respectively, and observe the differences between0,NULL, andnullptr. - Learn
enum class: Replace a plain enum with an enum class, and see which implicit conversions are prohibited. - Learn smart pointers: First write a manual
returnexample thatnew/deleteearly, then change it tounique_ptr. - Learn Lambda: Use capture by value and capture by reference respectively, and observe whether external variables change.
- Learn
std::function: Put multiple different lambdas into the samevectorand execute them uniformly. - Learn concurrency: Have two tasks that each wait 1 second execute sequentially first, then put them into two threads and execute them, and compare the elapsed time.