Skip to content

Wiki

ModernC++

About 17 min read

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:

  1. The example code includes complete #include.
  2. The example code includes the main function.
  3. The example code can be copied into a single .cpp file for compilation and execution.
  4. The running result is given below each example code.
  5. 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()。运行这些例程前,需要同时满足两件事:

  1. 当前 target 使用 C++23,例如在它的 CMake 配置中添加 target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_23)。
  2. 实际编译器和标准库已经实现相应功能。只改 -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:

  • nullptr Let null pointers and integers 0 be separated.
  • enum class Prevent 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::function Store and pass callable objects of different types in a unified way.
  • std::format / std::print Make 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:

  1. Where is the old syntax prone to errors?
  2. How does the modern syntax encode intent into types or grammar?
  3. 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:

  • auto is not weak typing; it just lets the compiler write types for you.
  • nullptr represents only a null pointer, not an integer.
  • enum class is 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, and map elements.

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:

  1. RAII: Bind resource and object lifetimes.
  2. unique_ptr : Exclusive ownership, the default choice.
  3. shared_ptr : Use only when multiple owners are genuinely needed.
  4. weak_ptr : Observe only, do not extend lifetime, used to break reference cycles.
  5. 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::print than stringstream Concise, than printf Type-safe.
  • std::chrono Avoid handwritten time unit conversions.
  • Concurrent programming must first ensure correctness, then consider speed.
  • std::filesystem Make 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

  1. Learn nullptr: Write two overloaded functions, receiving int and int* respectively, and observe the differences between 0 , NULL , and nullptr.
  2. Learn enum class: Replace a plain enum with an enum class, and see which implicit conversions are prohibited.
  3. Learn smart pointers: First write a manual return example that new/delete early, then change it to unique_ptr.
  4. Learn Lambda: Use capture by value and capture by reference respectively, and observe whether external variables change.
  5. Learn std::function: Put multiple different lambdas into the same vector and execute them uniformly.
  6. 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.