1. 类型萃取1.1 核心概念类型萃取这是 C++11 引入的特性(并在 C++14/17/20 中得到了极大的扩展),是进行 模板元编程 和编写通用泛型代码的基础工具。它提供了一系列编译时的模板类和模板别名,用于在编译期间检查、修改和操作类型。这些功能主要集中 C++ 标准库的 <type_traits> 头文件中。
在 C++ 中文技术社区和文献中,<type_traits> 通常有以下几种常见的称呼,根据上下文略有不同:
官方翻译:类型萃取,意为从类型中提取信息。
在 C++ 标准库中文版文档以及许多经典书籍(如《C++ Primer》译本)中,通常使用这个词来描述这种在编译期获取类型特性的机制。
编程术语:类型特性
<type_traits> 头文件提供的正是各种“类型”的“特性”(比如是否有 const 修饰符、是否是整数、是否是类等)。
通俗称呼:类型工具 或 类型辅助
在日常开发交流中,程序员很少专门说出一个学术名词,通常会说用一下 type_traits或用类型工具判断一下。
类型萃取允许在编 ...
1. 什么是 constexpr if简单来说,constexpr if 是 C++17 引入的一个编译期判断语句。它的作用是告诉编译器:在编译代码的时候,就根据这个条件决定保留哪一段代码,扔掉哪一段代码。
打个通俗的比方,想象你在打包行李(编译代码):
普通的 if 语句:就像把两把伞都放进了箱子,一把是遮阳伞,一把是雨伞。等到了目的地(程序运行时),看天气决定用哪一把。两把伞都得背着,箱子很重。
constexpr if 语句:就像在打包前看了一下天气预报。如果目的地是大晴天,就只把遮阳伞放进去,雨伞直接扔一边不带了。这样箱子(可执行文件)更轻,而且在目的地绝对不会误拿雨伞。
在 C++17 之前,如果用了模板(泛型),编写函数时往往需要写很多在这个特定模板类型下根本用不到的代码,或者会因为类型不同而导致编译错误。
constexpr if 允许我们将那些对当前类型来说非法的代码直接在编译阶段剔除掉,于是编译器就不会检查那些被剔除的代码的语法正确性。
它的写法和普通 if 几乎一样,只是开头加了一个 constexpr 关键字:
12345678if constexpr (条件 ...
1. 为什么需要内联变量1.1 缘起1.1.1 全局变量的问题在开始讲解内联变量之前,我们先来看一个让很多 C++ 初学者头疼的问题。假设你想在头文件中定义一个常量,你会怎么写?
12// my_constants.hconst int MAX_USERS = 100; // 这样做会有什么问题吗?
如果你在不同的源文件中包含了这个头文件,每个源文件都会得到自己的 MAX_USERS 副本。在 C++ 中,有一个重要的规则叫做 ODR(One Definition Rule,单一定义规则)。简单来说:
每个变量/函数在整个程序中只能有一个定义
可以有多个声明(告诉编译器这个变量/函数存在)
问题来了:如果我们在头文件中定义了一个变量,并且这个头文件被多个源文件包含,那么我们就违反了 ODR!
在 C++17 之前,我们通常这样解决这个问题:
12345// my_constants.hextern const int MAX_USERS; // 只是声明,不是定义// my_constants.cppconst int MAX_USERS = 100; ...
1. if/switch 初始化器C++17 引入了一个非常实用且优雅的小特性:在 if 和 switch 语句中直接进行变量初始化。虽然这个改动看起来不大,但它能让你的代码更安全、更简洁,还能避免变量作用域污染。
在 C++17 之前,我们写代码时经常会遇到这样一个尴尬的场景:我想检查一个变量是否满足某个条件,但这个变量本身也需要先被初始化。假设我们要从一个文件中查找一个值,并检查它是否存在。你可能会这么写:
123456789101112131415161718192021#include <iostream>#include <map>#include <string>int main() { std::map<int, std::string> my_data = {{1, "Apple"}, {2, "Banana"}}; // --- 痛点写法 (C++17之前) --- / ...
1. 结构化绑定基本语法C++17 引入了一个非常有趣且实用的特性,叫做结构化绑定。简单来说,它让我们可以像给变量起名字一样,一次性把一个复杂对象(如数组、结构体、元组)里的多个值拆分出来,分别赋值给不同的变量。
在 C++17 之前,如果我们想从一个包含两个值的对象里取值,代码可能会有点啰嗦。有了结构化绑定,代码瞬间变得像 Python 或 JavaScript 一样优雅。
结构化绑定就像是一个特殊的变量声明。它的基本长这样:
1auto [变量1, 变量2, 变量3, ...] = 要绑定的对象;
auto:让编译器自动推导类型(就像你平时用 auto a = 10; 一样)。
[ ... ]:方括号里是你想要的新变量名。
=:赋值符号。
注意: 方括号里的变量名数量,必须和你要绑定的对象里的元素数量完全一致,否则会报错。
假设现在有一个函数,它返回了一个包含学生姓名和分数的结构体:
C++17 之前(传统写法):
1234Student result = getStudent();// 得这样写,很啰嗦std::string name = result.name;in ...
1. 读写锁1.1 读写锁模型在多线程编程中,普通的 std::mutex 是独占锁。同一时间,只允许一个线程访问数据。然而,对于读多写少的数据(例如配置文件、缓存数据),这是低效的,因为读取操作本身是线程安全的,只需要防止在读取的过程中被写入打断即可。
std::shared_timed_mutex 和 std::shared_lock 是 C++14 引入的一对组合,用于实现读写锁机制。读写锁机制允许通过区分读操作和写操作来提高程序的并发性能。其核心思想是:读操作是共享的,写操作是独占的。
独占模式:
对应 写操作。
如果一个线程获得了独占锁(写锁),其他任何线程都无法获得共享锁(读)或独占锁(写)。
配合类:通常使用 std::unique_lock。
共享模式:
对应 读操作。
如果一个线程获得了共享锁(读锁),其他线程可以同时获得共享锁来读取数据。
但是,其他线程无法获得独占锁(写),直到所有共享锁被释放。
配合类:必须使用 std::shared_lock。
操作类型
线程 A (读)
线程 B (读)
线程 C (写)
场景 1
✅ 可以进入
✅ ...
1. 何为透明在 C++14 之前,标准库中的函数对象(如 std::less)是单态的,这意味着你必须在使用时指定具体的类型。C++14 引入了透明版本,即模板参数为 void 的版本(如 std::less<void>)。
所谓的透明,指的是该函数对象不关心参数的具体类型。它就像一个透明的代理,直接将参数传递给底层的运算符(如 operator<),而不强制进行类型转换。
不透明(C++11):std::less<int>
必须传入 int 类型。如果你传一个 short 或者 double,虽然能编译通过(因为有隐式转换),但可能会产生临时对象,或者在关联容器中因为类型不匹配而找不到。
透明(C++14):std::less<void>
它可以接受任意类型的参数,只要这两个参数能被 operator< 比较。
它内部直接调用 std::forward<T>(t) < std::forward<U>(u),不进行任何类型转换。
1.1 为什么需要透明C++14 中关于透明操作符函数对象的引入解 ...
1. unique_ptr在 C++11 引入智能指针(std::unique_ptr, std::shared_ptr, std::weak_ptr)奠定了现代 C++ 内存管理的基础后,C++14 主要在易用性、性能和功能细节上进行了重要的优化和增强。
在 C++11 中,虽然智能指针 std::unique_ptr 是标准的一部分,但创建它的辅助函数 std::make_unique 直到 C++14 才加入。
1.1 在 C++11 中创建对象在 C++11 中,创建一个unique_ptr类型的对象我们有以下几种选择:
方案 A:手动实现 make_unique
12345678910111213141516#include <memory>#include <iostream>// C++11 兼容的 make_unique 实现template<typename T, typename... Args>std::unique_ptr<T> make_unique(Args&&... args) { ...
1. 聚合类在 C++ 中,聚合类 是一种特殊的类类型,它主要用于表示数据的集合,类似于 C 语言的结构体。聚合类的定义非常严格,在C++11 及以后标准中,判定一个类型是否为聚合类通常遵循以下规则:
无用户自定义的构造函数:不能有用户显式提供的构造函数。
非私有/保护的非静态数据成员:所有的非静态数据成员必须是 public 的。
无虚函数:不能有虚函数。
无虚、私有或保护的基类:不能有虚基类,且基类必须是 public 的。
满足上述条件的类、结构体或数组,被称为聚合类。聚合类可以使用花括号初始化列表进行初始化。
123456789101112131415161718192021#include <iostream>struct Point // 这是一个普通的聚合类{ int x; int y;};int main() { // 数组作为聚合的演示 int arr[3] = {10, 20, 30}; // 合法的聚合初始化 // 普通聚合类的演示 Point ...
1. 基本语法在 C++14 之前,我们只能定义函数模板、类模板。C++14 引入了第三种模板变量模板,它允许我们将变量定义为模板。这使得我们可以创建类型相关的常量值或变量,大大增强了模板元编程的能力。
变量模板的声明方式类似于函数模板或类模板,只是在开头使用了 template <...> 和随后的变量声明。
基本语法格式如下:
12345template<typename T>constexpr T pi = T(3.1415926535897932385L);template<typename T>T special_value = T(42);
1.1 简单的变量模板1234567891011121314151617#include <iostream>using namespace std;template<typename T>constexpr T pi = T(3.1415926535897932385L);int main() { // 实例化为 double 类型的 pi doubl ...








































