GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - feixiao/ModernCpp: 现代C++

BranchesTags
Open more actions menu

Latest commit

 

History

19 Commits
19 Commits

Folders and files

NameName
Last commit message
Last commit date
src
src
 
 
.gitignore
.gitignore
 
 
CMakeLists.txt
CMakeLists.txt
 
 
LICENSE
LICENSE
 
 
ReadMe.md
ReadMe.md
 
 
模块(C++20)-补充内容.pdf
模块(C++20)-补充内容.pdf
 
 

Repository files navigation

现代 C++ 快速入门

本仓库收集了 275 个现代 C++ 示例文件,内容覆盖 C++11、C++14、C++17 和 C++20。示例按章节放在 src 目录中,适合围绕单个语言特性阅读、修改和验证。

参考资料:《Modern C++ Tutorial: C++11/14/17/20》

学习每个特性时,不只看“语法怎么写”,还要回答三个问题:它解决什么问题、什么时候适合用、相较原有写法有什么具体好处。下面的章节索引按这三个维度组织,并保留源码入口。

目录结构

.
├── CMakeLists.txt       # 7 个早期示例的构建入口
├── ReadMe.md            # 项目说明和源码导航
└── src/
    ├── 1-1.cpp
    ├── 2-1.cpp
    ├── 2-2.cpp
    └── ...              # 共 42 章、275 个示例

src/<章节>-<序号>.cpp 是统一命名规则。例如,6-1.cpp 到 6-9.cpp 都属于第 6 章,主题是移动语义和完美转发。同一章通常通过一组相近代码展示正确写法、错误写法及版本差异。

5 分钟熟悉 src

  1. 从下方章节索引定位感兴趣的语言特性。
  2. 先读该章的语法作用和使用场景,再打开源码入口,识别 main()、核心类型和关键表达式。
  3. 按编号比较同章示例:原有写法有什么成本,新特性减少了哪些重复、错误或资源开销?
  4. 单独编译并运行一个示例,不要直接假设整章都能批量构建。
  5. 修改输入、类型或限定符,再观察编译结果和输出。

以下命令适合快速查看和比较一章代码:

# 列出第 6 章全部示例
printf '%s\n' src/6-*.cpp

# 查找第 41 章中的 Concepts 和约束表达式
rg -n 'concept|requires' src/41-*.cpp

# 比较两个相邻示例
diff -u src/21-1.cpp src/21-2.cpp

章节索引

这里的“传统写法”指引入对应特性前的常见实现:多数以 C++98/03 为参照,C++17/20 特性也会与 C++11/14 写法比较。numeric_limits、链式调用等本来就能用于传统 C++,不应当作新标准独有的能力。收益也不等于运行得更快;很多特性的价值在于类型安全、作用域控制和维护成本。

章节与源码入口 语法作用 典型使用场景 传统写法 → 具体好处
1 · 整数边界 用 numeric_limits<T> 查询类型取值范围等属性 泛型数值算法、计算前检查边界 硬编码边界或按类型选宏 → 统一按类型查询,减少平台假设;它本身不阻止溢出
2 · 命名空间 组织名称;内联命名空间可暴露默认版本,嵌套语法简化声明 库的 API 版本管理、分层组织代码 名称前缀、手工转发和层层嵌套声明 → 隔离冲突,简化默认版本访问;普通命名空间早已存在
3 · auto 非类型模板参数 C++17 起由传入的编译期值推导模板参数类型 编译期常量包装、以值配置模板 template<class T, T N> → template<auto N>,调用者不必重复提供值的类型
4 · decltype(auto) C++14 起按 decltype 规则推导类型,可保留引用 转发函数返回值、包装访问器 手写返回类型或复杂类型萃取 → 随表达式保留类型;括号会影响推导,仍须防止悬垂引用
5 · 返回类型推导 用尾置返回类型表达参数相关类型;C++14 支持 auto 返回推导 泛型运算、返回类型很长的辅助函数 手写返回类型、辅助 traits → 减少重复声明,让接口跟随表达式类型
6 · 移动与完美转发 T&&、std::move、std::forward 支持资源转移及值类别传递 大缓冲区、独占资源、泛型工厂 深拷贝、手工 swap、成组重载 → 类型支持移动时可避免复制资源;转发可减少重载重复
7 · Lambda 就地定义可调用对象并捕获上下文 排序规则、算法谓词、局部回调 单独函数或具名函数对象 → 行为与使用位置放在一起,减少样板;引用捕获须关注生命周期
8 · 成员默认初始化 在非静态成员声明处给出默认初值 多个构造函数共享默认状态 每个构造函数重复初始化 → 集中维护默认值,减少漏初始化;构造函数仍可覆盖它
9 · 列表初始化 用花括号初始化对象,支持 initializer_list 并限制窄化转换 容器初值、对象构造、数值转换检查 多种初始化形式、隐式截断 → 写法更一致且可在编译期拒绝窄化;列表构造函数优先级可能改变含义
10 · default / delete 显式请求默认特殊成员函数,或禁止某个函数参与有效调用 禁止复制的资源类型、限制不希望的重载 私有且不定义的拷贝函数、手写空函数 → 意图更清楚,误用直接报错;合适的默认化声明可保留平凡性
11 · 联合体生命周期 允许联合体含非平凡成员,但需管理活动成员的构造与析构 带标签的紧凑存储、底层内存布局 原始字节缓冲和强制转换 → 能声明真实成员类型;管理仍复杂,通常优先考虑 C++17 std::variant
12 · 委托构造 一个构造函数委托同类的另一个构造函数完成初始化 多种构造入口共享初始化逻辑 重复初始化列表或构造后调用 init() → 复用真正的构造过程,集中建立对象不变量
13 · 继承构造 用 using Base::Base 引入基类构造函数;using 也可引入名称 不改变构造接口的薄派生类 为基类每个构造函数写转发版本 → 减少重复;派生类新增成员仍需正确初始化
14 · 强类型枚举 enum class 限定枚举项作用域,禁止隐式转整数,可指定底层类型 状态、错误码、协议字段类型 普通枚举泄漏名称并可混入整数运算 → 减少命名冲突和跨枚举误用,转换意图更明确
15 · 聚合初始化 按聚合规则直接初始化成员;C++20 支持指定成员初始化 简单配置、记录、数据载体 为纯数据类型编写样板构造函数或逐个赋值 → 直接表达初始数据;聚合资格随标准版本变化
16 · override / final 检查虚函数重写,或禁止继续重写、派生 多态接口、需要限制扩展的类型 靠人工核对签名和注释约定 → 编译器发现参数或限定符不匹配,强制执行扩展边界
17 · 范围 for 通过范围的迭代接口逐元素遍历 数组、容器及自定义范围 手写下标或迭代器循环 → 减少边界与递增错误;用 auto& / const auto& 避免不必要的元素复制
18 · 条件初始化语句 C++17 的 if / switch 可在条件前声明局部对象 查表后判断结果、带锁检查状态 提前声明临时变量或额外套作用域 → 将变量寿命限制在整个条件语句内,减少名称泄漏
19 · static_assert 在编译期检查常量条件并给出诊断 模板参数限制、大小与布局假设 运行期 assert 或负长度数组技巧 → 问题在编译时暴露,表达更直接且无运行期开销
20 · 元组与结构化绑定 tuple 组织多值;C++17 用 auto [a, b] 分解对象 多返回值、遍历键值对 输出参数、get<0> 或先声明再 tie → 为各部分直接命名并推导类型,减少位置索引与重复声明
21 · noexcept 声明不抛异常的契约,可按表达式计算条件 移动操作、swap、泛型异常保证 动态异常说明或仅写注释 → 可查询的编译期契约,使容器等能选择安全的移动路径;违约会终止程序
22 · 类型别名 用 using 定义别名及别名模板 简化复杂类型、复用容器与策略组合 typedef 和嵌套 type 的辅助模板 → 声明更易读,模板别名可直接使用;不会产生新类型
23 · nullptr 提供专门的空指针字面量及类型 指针初始化、重载调用、泛型传参 0 / NULL 兼有整数含义 → 避免误选整数重载,表达空指针意图更准确
24 · 三路比较 C++20 的 <=> 表达比较类别,可默认化成员比较 值类型、排序键、复合记录 手写多组关系运算符 → 减少重复和相互矛盾;默认成员顺序须符合业务语义,浮点数可能是偏序
25 · thread_local 为每个线程提供独立对象及线程存储期 线程私有缓存、统计、临时状态 平台 TLS API 或以线程 ID 管理映射 → 标准化声明与生命周期管理;不能据此保证所指向的共享数据安全
26 · 内联变量 C++17 允许满足规则的变量定义出现在多个翻译单元 仅头文件库的共享变量、静态数据成员 头文件 extern 加一个 .cpp 定义 → 可在头文件直接定义同一实体,简化链接组织;inline 不表示更快
27 · 常量求值 constexpr 支持常量求值,C++17 if constexpr 按条件丢弃分支 编译期表与参数、按类型选择模板实现 宏、枚举常量、模板特化或标签分派 → 用更普通的表达式和控制结构表达编译期逻辑;constexpr 函数也可在运行期执行
28 · 链式调用 返回当前对象的引用等结果以支持连续成员调用 字符串操作、配置接口 多条重复对象名的调用 → 紧凑表达连续操作;传统 C++ 已支持,收益来自接口设计而非新标准
29 · 字面量扩展 十六进制浮点表达二进制数值,分隔符改善读数,自定义后缀创建值 精确浮点常量、大数值、单位与领域类型 难读的数字和无单位裸值 → 更易核对数值;后缀配合专用类型可表达单位,单靠后缀不自动保证安全
30 · 内存对齐 alignas 指定对齐,alignof 查询要求,对齐分配支持过度对齐对象 SIMD 数据、硬件接口、自定义存储 编译器专用属性和手工地址调整 → 标准方式表达与检查对齐;性能收益仍需测量
31 · 标准属性 用 [[noreturn]]、[[deprecated]]、[[nodiscard]] 等表达意图 标记废弃接口、提醒处理结果、说明控制流 注释或厂商属性 → 可移植地向工具提供信息并获得相应诊断;并非所有属性都会强制报错
32 · 头文件检测 用 __has_include 在预处理时检测头文件是否可用 可选依赖、跨工具链构建 按编译器名称或版本猜测 → 直接探测头文件;是否支持某项 API 仍需特性宏或构建检查
33 · 协程 C++20 用 co_await / co_yield 等表达挂起和恢复 异步 I/O、惰性生成、异步流程组合 回调链或手写状态机 → 用接近顺序代码的形式表达可挂起流程;需要协程返回类型与调度支持,不自动创建线程
34 · 初始化与生命周期 用 explicit 控制隐式转换,并理解复制消除与对象构造规则 转换敏感的 API、工厂返回值、资源对象 依赖隐式转换或为避拷贝使用输出参数 → 接口意图更明确;C++17 指定纯右值场景保证直接构造,具名局部变量的 NRVO 不保证
35 · 可变参数模板 参数包支持任意数量的模板参数,C++17 折叠表达式组合运算 类型安全的日志、构造转发、多值运算 C 风格 ...、宏或固定数量重载 → 保留参数类型并减少重复;折叠表达式进一步减少递归模板
36 · 模板模板参数 将模板本身作为参数以注入类型构造方式 容器适配、可替换的模板策略 对每种容器重复实现或手工包装 → 算法与类型构造方式分离;该能力在 C++98 已有,后续标准放宽部分匹配规则
37 · 非类型及默认模板参数 用编译期值和默认参数配置模板;新标准扩展可接受的值类型 固定容量、编译期策略、减少常见调用参数 宏配置、重复特化或冗长实参列表 → 将配置纳入类型检查并提供默认值;传统 C++ 已支持其基础形式
38 · 模板实参推导 按实参推导模板参数,C++17 扩展到类模板构造 创建容器、包装器和泛型对象 重复写类型参数或依赖 make_* 工厂 → 降低调用噪声,减少声明与实参类型不同步
39 · 推导指引 C++17 显式规定构造实参应推导出哪个类模板实例 迭代器区间构造、自定义包装类型 要求调用者手填类型或额外提供工厂 → 将推导规则放在类型旁边,让构造接口保持简洁
40 · SFINAE 替换失败时排除候选,结合表达式检测选择重载 兼容旧标准的泛型库、检测类型操作能力 宏判断或大量显式特化 → 按类型能力选择实现;规则较隐晦,C++20 常可改用约束表达意图
41 · Concepts 与约束 C++20 用 concept / requires 命名并检查模板要求 公共泛型 API、按能力选择重载 enable_if、检测惯用法和间接替换失败 → 要求直接出现在接口上,约束可复用,诊断通常更接近调用原因
42 · 模板语法演进 对照嵌套 >>、变量模板及不同约束写法 迁移旧模板代码、封装编译期查询 尖括号间手工空格、trait<T>::value、复杂约束声明 → 减少语法噪声;具体支持版本应逐例核对

把语法放回使用场景

下面三组对照展示如何从“会写”进一步判断“为什么用”。

结构化绑定:接收多个返回值

场景:函数同时返回两个坐标,调用者需要分别命名并使用它们。

  • 原有写法:20-1.cpp 使用 C++11 的 tuple,先声明 int x, y,再用 std::tie(x, y) 接收结果。
  • 新写法:20-2.cpp 用 C++17 的 auto [x, y] = return_multiple_values(); 同时完成声明、类型推导与分解。
  • 好处:不用重复声明元素类型,也不用在后续代码中用 get<0>、get<1> 表示含义;主要收益是可读性,不应宣称必然更快。
  • 边界:结构化绑定会引入新名称;给已有变量赋值时,std::tie 仍适用。分解已有对象时,auto [x, y] = obj 通常会复制对象,需共享或修改原对象时应考虑引用形式。

移动语义:转移大对象的资源

场景:把一个不再需要的大缓冲区交给另一个对象。如果执行深拷贝,需要新分配内存并复制数据;类型提供移动操作后,可以转交资源所有权。

  • 语法作用:std::move(obj) 把表达式转换为可参与移动重载选择的形式,真正转移资源的是被选中的移动构造或移动赋值函数。
  • 好处:6-5.cpp 的移动构造只接管指针并清空源指针,相比其复制构造中的分配与 memcpy,可避免复制整个缓冲区。这个收益以允许转移所有权为前提。
  • 边界:同一文件的 get_pool(const BigMemoryPool& pool) 中,std::move(pool) 得到的是 const 右值,不能绑定到通常的 BigMemoryPool&& 移动构造参数,因此这里仍会调用复制构造。不能仅凭出现 std::move 就认定消除了拷贝。
  • 使用判断:仍需保留独立副本时应复制;只需只读访问时可传 const&。移动后源对象的可用操作取决于类型契约,不能假设值保持不变或一定为空。

Concepts:把模板要求写进接口

场景:模板只接受整数类型,希望调用处就能看清限制,并在传入不符合要求的类型时得到约束诊断。

  • 原有写法:41-1.cpp 用 enable_if_t 配合 is_integral_v 表达限制,把要求放进默认模板参数;这种机制源自 C++11,其中的 _t、_v 简写分别需要 C++14、C++17。
  • 新写法:41-2.cpp 在 C++20 中定义 IntegerType 概念,再用 template <IntegerType T> 直接约束参数。
  • 好处:约束有名称、可复用,接口更容易理解;不满足要求时,编译器通常能直接指出失败的约束。两者都是编译期机制,收益主要是接口表达和维护性。
  • 边界:约束只能验证写出的类型或表达式要求,不能证明业务正确性,例如一个比较器是否真的满足严格弱序;仍需实现者保证语义。

编译和运行

使用 CMake 构建已配置示例

根目录的 CMakeLists.txt 已升级为 C++20,统一收录了全部 157 个可独立编译运行的示例目标(排除了预期编译失败的反例及无 main() 的代码片段)。

目标命名规则为 ex<章节两位>_<序号两位>,例如 src/20-2.cpp 对应目标 ex20_02,同时兼容旧版别名(如 ex01, ex02-01 等):

# 1. 生成构建配置(必须使用独立构建目录)
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release

# 2. 一键编译全部 157 个示例目标
cmake --build build -j

# 3. 运行指定示例
./build/ex20_02
./build/ex06_05

单独编译任意示例

学习 src 时,推荐直接编译单个文件。C++20 可以覆盖多数示例使用的语言特性:

mkdir -p build/single
c++ -std=c++20 -Wall -Wextra src/29-2.cpp -o build/single/29-2
./build/single/29-2

涉及线程的示例通常还需要 -pthread:

c++ -std=c++20 -pthread src/25-3.cpp -o build/single/25-3
./build/single/25-3

阅读时的注意事项

  • 一些示例故意展示编译失败,例如错误的 override、失败的 static_assert 或不满足 Concepts 的类型。源码注释通常会标出“编译失败”或“编译错误”。
  • 一些文件是代码片段,不包含完整的头文件或 main();它们用于解释语法,不是独立程序。
  • 少数示例依赖编译器扩展、平台相关汇编或早期 <experimental/...> 头文件,现代编译器不一定继续提供对应接口。
  • 部分文件在注释中保留了汇编或 GIMPLE 输出,用于观察语言特性对生成代码的影响。
  • 遇到失败时,先判断它是教学反例、标准版本差异,还是平台/编译器扩展差异,再决定是否修改代码。

推荐阅读路线

  • 基础语法迁移:1 → 2 → 3 → 4 → 5 → 7 → 9 → 17 → 18 → 20。
  • 对象模型与性能:6 → 8 → 10 → 11 → 12 → 13 → 15 → 30 → 34。
  • 泛型编程:19 → 22 → 27 → 35 → 36 → 37 → 38 → 39 → 40 → 41 → 42。
  • C++20 重点:24 → 27 → 31 → 33 → 37 → 41 → 42。

About

现代C++

Resources

Readme
Apache-2.0 license
Activity

Stars

0 stars

Watchers

1 watching

Forks

0 forks
Report repository

Releases

Packages

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.