目录
11. 字符串转换(std::from_chars/std::to_chars)
8. 指定初始化(Designated Initializers)
12. C++ 属性符([[likely]]/[[unlikely]])
13. 日历功能(std::chrono::year_month_day)
14. 时区功能(std::chrono::zoned_time)
一、C++11 概述
C++11 是 C++ 编程语言的重要标准,于 2011 年正式发布(此前曾用名 C++0x)。它是 C++ 自 1998 年标准后的首次重大更新,引入了大量新特性,旨在简化开发、提升性能、增强安全性,并原生支持并发编程,解决了 C++98/03 中的诸多痛点。
二、C++11 新增特性及代码示例
1. 简化使用
1.1 auto & decltype
auto:自动推导变量类型,简化声明。decltype:推导表达式类型,常用于模板或类型计算。
#include <iostream>
#include <vector>
int main() {
// auto:自动推导类型
auto a = 10; // 推导为 int
auto b = 3.14; // 推导为 double
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin(); // 推导为 vector<int>::iterator
// decltype:推导表达式类型
int x = 5;
decltype(x) y = 10; // y 类型为 int
decltype(x + b) z = 15.5; // z 类型为 double(int + double → double)
return 0;
}
1.2 C++11 for 循环(范围 for 循环)
简化容器遍历,直接迭代元素而非通过索引或迭代器。
#include <iostream>
#include <vector>
int main() {
std::vector<int> nums = {1, 2, 3, 4, 5};
// 范围 for 循环:遍历容器中所有元素
for (int num : nums) {
std::cout << num << " "; // 输出:1 2 3 4 5
}
std::cout << std::endl;
// 修改元素(需用引用)
for (int& num : nums) {
num *= 2;
}
for (int num : nums) {
std::cout << num << " "; // 输出:2 4 6 8 10
}
return 0;
}
1.3 智能指针
自动管理动态内存,避免内存泄漏。
- unique_ptr:独占所有权,不可复制,只能移动。
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> ptr1(new int(10)); // 独占指针
// std::unique_ptr<int> ptr2 = ptr1; // 错误:不能复制
std::unique_ptr<int> ptr2 = std::move(ptr1); // 可以移动(ptr1 变为空)
if (ptr1) std::cout << *ptr1 << std::endl; // 不执行(ptr1 为空)
if (ptr2) std::cout << *ptr2 << std::endl; // 输出:10
return 0;
}
- shared_ptr:共享所有权,通过引用计数管理生命周期。
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> ptr1(new int(20));
std::cout << "引用计数:" << ptr1.use_count() << std::endl; // 输出:1
std::shared_ptr<int> ptr2 = ptr1; // 共享所有权
std::cout << "引用计数:" << ptr1.use_count() << std::endl; // 输出:2
ptr1.reset(); // 释放 ptr1 的所有权
std::cout << "引用计数:" << ptr2.use_count() << std::endl; // 输出:1
return 0;
}
- weak_ptr:弱引用,不增加引用计数,用于解决 shared_ptr 的循环引用问题
#include <iostream>
#include <memory>
struct Node {
std::weak_ptr<Node> next; // 弱引用,避免循环引用
~Node() { std::cout << "Node 被销毁" << std::endl; }
};
int main() {
std::shared_ptr<Node> node1(new Node());
std::shared_ptr<Node> node2(new Node());
node1->next = node2; // weak_ptr 不增加引用计数
node2->next = node1;
return 0; // 离开作用域时,node1 和 node2 引用计数归 0,正常销毁
}
1.4 统一初始化
使用 {} 进行初始化,适用于变量、数组、对象等,避免歧义。
#include <iostream>
#include <vector>
#include <map>
struct Point {
int x;
int y;
};
int main() {
// 基础类型初始化
int a{10}; // 等价于 int a = 10;
// 数组初始化
int arr[]{1, 2, 3, 4}; // 长度自动推导
// 容器初始化
std::vector<int> vec{1, 2, 3}; // 直接初始化元素
std::map<std::string, int> map{{"a", 1}, {"b", 2}};
// 结构体初始化
Point p{10, 20}; // 无需构造函数,直接初始化成员
return 0;
}
1.5 nullptr
专门用于初始化空指针,替代 NULL(避免 NULL 被定义为 0 导致的类型歧义)。
#include <iostream>
void func(int x) {
std::cout << "int 重载:" << x << std::endl;
}
void func(char* ptr) {
std::cout << "char* 重载:" << ptr << std::endl;
}
int main() {
// NULL 会被解析为 0,调用 int 重载
func(NULL); // 输出:int 重载:0
// nullptr 明确为指针类型,调用 char* 重载
func(nullptr); // 输出:char* 重载:(null)
return 0;
}
1.6 constexpr
声明常量表达式,在编译期计算,提升性能。
#include <iostream>
// 编译期计算的常量表达式函数
constexpr int factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int val = factorial(5); // 编译期计算,结果为 120
std::cout << val << std::endl; // 输出:120
int n = 5;
// constexpr int val2 = factorial(n); // 错误:n 是变量,无法在编译期计算
return 0;
}
1.7 explicit
阻止单参数构造函数的隐式转换,增强代码安全性。
#include <iostream>
class A {
public:
explicit A(int x) : m_x(x) {} // explicit 阻止隐式转换
int getX() const { return m_x; }
private:
int m_x;
};
void printA(A a) {
std::cout << a.getX() << std::endl;
}
int main() {
A a(10);
printA(a); // 正确:显式传递 A 对象
// printA(20); // 错误:explicit 阻止 int 隐式转换为 A
printA(A(20)); // 正确:显式构造 A 对象
return 0;
}
1.8 final & override
final:阻止类被继承或函数被重写。override:显式声明重写基类虚函数,避免拼写错误。
#include <iostream>
// final:阻止类被继承
class Base final {
public:
virtual void func() {
std::cout << "Base::func()" << std::endl;
}
};
// class Derived : public Base {}; // 错误:Base 被 final 修饰,不能继承
class Base2 {
public:
virtual void func() {
std::cout << "Base2::func()" << std::endl;
}
virtual void func2() final { // final:阻止该函数被重写
std::cout << "Base2::func2()" << std::endl;
}
};
class Derived2 : public Base2 {
public:
void func() override { // override:明确重写基类函数
std::cout << "Derived2::func()" << std::endl;
}
// void func2() override {} // 错误:func2 被 final 修饰,不能重写
};
int main() {
Base2* obj = new Derived2();
obj->func(); // 输出:Derived2::func()
obj->func2(); // 输出:Base2::func2()
delete obj;
return 0;
}
1.9 右值引用
用 && 表示,绑定到临时对象(右值),用于移动语义和完美转发。
#include <iostream>
// 右值引用函数
void func(int&& x) {
std::cout << "右值引用:" << x << std::endl;
}
int main() {
int a = 10;
// func(a); // 错误:a 是左值,不能绑定到右值引用
func(20); // 正确:20 是右值
func(a + 5); // 正确:a+5 是右值(临时结果)
return 0;
}
1.10 移动语义
通过移动构造函数和移动赋值运算符,转移资源所有权,避免不必要的拷贝。
#include <iostream>
#include <vector>
class MyVector {
private:
int* data;
size_t size;
public:
// 构造函数
MyVector(size_t n) : size(n), data(new int[n]) {
std::cout << "构造函数:分配 " << n << " 个元素" << std::endl;
}
// 移动构造函数(参数为右值引用)
MyVector(MyVector&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr; // 转移所有权后,原对象资源置空
other.size = 0;
std::cout << "移动构造函数:转移资源" << std::endl;
}
~MyVector() {
if (data) {
delete[] data;
std::cout << "析构函数:释放 " << size << " 个元素" << std::endl;
}
}
};
int main() {
MyVector v1(1000); // 调用构造函数
MyVector v2 = std::move(v1); // 调用移动构造函数(v1 资源被转移)
return 0;
}
1.11 完美转发
通过 std::forward 保持参数的左值 / 右值特性,常用于模板函数。
#include <iostream>
#include <utility>
void process(int& x) {
std::cout << "处理左值:" << x << std::endl;
}
void process(int&& x) {
std::cout << "处理右值:" << x << std::endl;
}
// 完美转发模板
template <typename T>
void forward_func(T&& arg) {
process(std::forward<T>(arg)); // 保持 arg 的原始特性
}
int main() {
int a = 10;
forward_func(a); // 传递左值 → 调用 process(int&)
forward_func(20); // 传递右值 → 调用 process(int&&)
forward_func(std::move(a)); // 显式转为右值 → 调用 process(int&&)
return 0;
}
2. 并发支持
2.1 内存模型(原子操作)
std::atomic 提供原子操作,确保多线程下的数据一致性。
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> count(0); // 原子变量,多线程安全
void increment() {
for (int i = 0; i < 10000; ++i) {
count++; // 原子操作,无需加锁
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "最终计数:" << count << std::endl; // 稳定输出 20000
return 0;
}
2.2 线程与锁
std::thread 原生支持线程,std::mutex 提供互斥锁。
#include <iostream>
#include <thread>
#include <mutex>
int shared_data = 0;
std::mutex mtx; // 互斥锁
void increment() {
for (int i = 0; i < 10000; ++i) {
std::lock_guard<std::mutex> lock(mtx); // 自动加锁/解锁
shared_data++;
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "共享数据结果:" << shared_data << std::endl; // 输出 20000
return 0;
}
2.3 期值(future)
std::future 和 std::async 用于获取异步操作的结果。
#include <iostream>
#include <future>
#include <chrono>
// 异步执行的函数
int calculate() {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时计算
return 100;
}
int main() {
// 异步启动任务(默认策略:可能创建新线程)
std::future<int> fut = std::async(calculate);
std::cout << "等待计算结果..." << std::endl;
int result = fut.get(); // 阻塞等待结果
std::cout << "计算结果:" << result << std::endl; // 输出:100
return 0;
}
3. 改进对泛型编程的支持
3.1 lambda 表达式
匿名函数,简化代码(尤其在算法中作为回调)。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> nums = {3, 1, 4, 1, 5};
// lambda 作为排序谓词(降序)
std::sort(nums.begin(), nums.end(), [](int a, int b) {
return a > b;
});
// lambda 遍历输出
std::for_each(nums.begin(), nums.end(), [](int num) {
std::cout << num << " "; // 输出:5 4 3 1 1
});
return 0;
}
3.2 变参模板
支持任意数量的模板参数,用于泛型函数 / 类。
#include <iostream>
// 递归终止函数(处理0个参数)
void print() {
std::cout << std::endl;
}
// 变参模板函数(处理至少1个参数)
template <typename T, typename... Args>
void print(T first, Args... rest) {
std::cout << first << " ";
print(rest...); // 递归展开参数包
}
int main() {
print(1, 2.5, "hello", 'a'); // 输出:1 2.5 hello a
return 0;
}
3.3 别名(using)
替代 typedef,支持模板别名,更灵活。
#include <iostream>
#include <vector>
// 基础类型别名(类似 typedef)
using IntVec = std::vector<int>;
// 模板别名(typedef 无法实现)
template <typename T>
using MyMap = std::map<T, T>; // 键值类型相同的 map
int main() {
IntVec vec = {1, 2, 3}; // 等价于 std::vector<int>
MyMap<int> map; // 等价于 std::map<int, int>
map[1] = 10;
map[2] = 20;
return 0;
}
3.4 tuple
存储不同类型元素的容器,类似 “匿名结构体”。
#include <iostream>
#include <tuple>
#include <string>
int main() {
// 创建 tuple(包含 int、double、string)
std::tuple<int, double, std::string> t(10, 3.14, "hello");
// 获取元素(通过索引,从0开始)
std::cout << std::get<0>(t) << std::endl; // 输出:10
std::cout << std::get<1>(t) << std::endl; // 输出:3.14
std::cout << std::get<2>(t) << std::endl; // 输出:hello
// 解包 tuple
int a;
double b;
std::string c;
std::tie(a, b, c) = t; // a=10, b=3.14, c="hello"
return 0;
}
二、C++14 新增特性及代码示例
1. 函数返回值类型推导
C++14 允许函数用 auto 作为返回类型,编译器自动推导返回值类型(需保证所有 return 语句类型一致)。
#include <iostream>
// 自动推导返回类型(无需显式声明)
auto add(int a, double b) {
return a + b; // 推导为 double
}
auto multiply(double x, int y) {
return x * y; // 推导为 double
}
int main() {
std::cout << add(3, 2.5) << std::endl; // 输出:5.5
std::cout << multiply(2.5, 4) << std::endl; // 输出:10
return 0;
}
2. Lambda 参数支持 auto
C++14 允许 Lambda 表达式的参数使用 auto,使其成为泛型 Lambda(编译器自动推导参数类型)。
#include <iostream>
#include <string>
int main() {
// 泛型 Lambda:可接受任意类型参数
auto sum = [](auto a, auto b) {
return a + b;
};
std::cout << sum(1, 2) << std::endl; // 输出:3(int+int)
std::cout << sum(3.5, 2.5) << std::endl; // 输出:6(double+double)
std::cout << sum(std::string("Hello "), "World") << std::endl; // 输出:Hello World
return 0;
}
3. 变量模板
C++14 允许定义模板变量,为不同类型生成对应的变量实例。
#include <iostream>
#include <cmath>
// 变量模板:不同类型的圆周率
template <typename T>
constexpr T pi = T(3.14159265358979323846);
int main() {
std::cout << pi<float> << std::endl; // 单精度 pi
std::cout << pi<double> << std::endl; // 双精度 pi
std::cout << pi<long double> << std::endl; // 长双精度 pi
return 0;
}
4. 别名模板
C++14 扩展了别名模板的灵活性,可用于简化复杂模板类型的声明。
#include <iostream>
#include <map>
// 别名模板:键和值类型相同的 map
template <typename T>
using SameTypeMap = std::map<T, T>;
int main() {
SameTypeMap<int> int_map; // 等价于 std::map<int, int>
int_map[1] = 10;
int_map[2] = 20;
SameTypeMap<std::string> str_map; // 等价于 std::map<std::string, std::string>
str_map["one"] = "1";
return 0;
}
5. constexpr 限制放宽
C++14 允许 constexpr 函数包含循环、局部变量和条件分支(C++11 仅允许单条 return 语句)。
#include <iostream>
// 编译期计算阶乘(支持循环)
constexpr int factorial(int n) {
int result = 1;
for (int i = 2; i <= n; ++i) { // C++14 允许 constexpr 函数中使用循环
result *= i;
}
return result;
}
int main() {
constexpr int val = factorial(5); // 编译期计算:120
std::cout << val << std::endl;
return 0;
}
6. [[deprecated]] 标记
用于标记不推荐使用的函数、变量或类型,编译器会生成警告。
#include <iostream>
// 标记为不推荐使用
[[deprecated("use new_func() instead")]]
void old_func() {
std::cout << "Old function" << std::endl;
}
void new_func() {
std::cout << "New function" << std::endl;
}
int main() {
old_func(); // 编译警告:'old_func' is deprecated: use new_func() instead
new_func();
return 0;
}
7. 二进制字面量与整形字面量分隔符
- 二进制字面量:用
0b前缀表示(如0b1010表示 10)。 - 整形分隔符:用
'分隔数字(如1'000'000表示 1000000),增强可读性。
#include <iostream>
int main() {
int bin = 0b1010'1111; // 二进制 10101111 = 十进制 175
int dec = 1'000'000; // 十进制 1000000
long long hex = 0x1234'5678'9ABC'DEF0; // 十六进制,用'分隔
std::cout << bin << std::endl; // 输出:175
std::cout << dec << std::endl; // 输出:1000000
return 0;
}
8. std::make_unique
C++14 引入的安全创建 std::unique_ptr 的函数(C++11 仅支持 std::make_shared),避免直接使用 new。
#include <iostream>
#include <memory>
struct MyClass {
int value;
MyClass(int v) : value(v) {}
};
int main() {
// 安全创建 unique_ptr(自动推导类型,避免裸 new)
auto ptr = std::make_unique<MyClass>(42);
std::cout << ptr->value << std::endl; // 输出:42
return 0;
}
9. std::shared_timed_mutex 与 std::shared_lock
支持 “多读单写” 模式:多个读者可同时访问,写者独占访问(带超时机制)。
#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>
std::shared_timed_mutex mtx;
int data = 0;
// 读者:共享访问
void reader(int id) {
std::shared_lock<std::shared_timed_mutex> lock(mtx); // 共享锁
std::cout << "Reader " << id << " reads: " << data << std::endl;
}
// 写者:独占访问
void writer() {
std::unique_lock<std::shared_timed_mutex> lock(mtx); // 独占锁
data++;
std::cout << "Writer updates data to: " << data << std::endl;
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) threads.emplace_back(reader, i);
threads.emplace_back(writer);
for (int i = 5; i < 10; ++i) threads.emplace_back(reader, i);
for (auto& t : threads) t.join();
return 0;
}
10. std::integer_sequence
生成编译期整数序列,用于模板元编程(如传递连续索引)。
#include <iostream>
#include <utility> // for integer_sequence
// 打印整数序列(递归展开)
template <int... Args>
void print_sequence(std::integer_sequence<int, Args...>) {
(std::cout << ... << Args) << std::endl; // C++17 折叠表达式(此处简化展示)
}
int main() {
// 生成 0~4 的整数序列
print_sequence(std::make_integer_sequence<int, 5>()); // 输出:01234
return 0;
}
11. std::exchange
交换两个值并返回旧值(原子操作,比手动交换更简洁)。
#include <iostream>
#include <utility> // for exchange
int main() {
int a = 10, b = 20;
int old_a = std::exchange(a, b); // a 变为 20,返回旧值 10
std::cout << "a = " << a << ", old_a = " << old_a << std::endl; // a=20, old_a=10
return 0;
}
12. std::quoted
为字符串添加引号(输出时自动添加,输入时自动去除引号)。
#include <iostream>
#include <iomanip> // for quoted
int main() {
std::string s = "Hello World";
std::cout << std::quoted(s) << std::endl; // 输出:"Hello World"(带引号)
std::string input;
std::cin >> std::quoted(input); // 输入 "test" 时,input 会被解析为 test(去引号)
return 0;
}
三、C++17 新增特性及代码示例
1. 构造函数模板推导
编译器可从初始化参数自动推导模板类型,无需显式指定。
#include <iostream>
#include <vector>
#include <pair>
int main() {
std::vector v = {1, 2, 3}; // 自动推导为 std::vector<int>(C++17 前需写 std::vector<int>)
std::pair p(10, "hello"); // 自动推导为 std::pair<int, const char*>
return 0;
}
2. 结构化绑定
分解 tuple、pair、数组或结构体的成员,直接绑定到变量。
#include <iostream>
#include <vector>
#include <map>
#include <tuple>
struct Point { int x; int y; };
int main() {
// 分解 pair
std::pair<int, std::string> p(1, "one");
auto [num, str] = p; // num=1, str="one"
// 分解 tuple
std::tuple<float, int, char> t(3.14, 5, 'a');
auto [f, i, c] = t; // f=3.14, i=5, c='a'
// 分解结构体
Point pt{10, 20};
auto [x, y] = pt; // x=10, y=20
// 遍历 map 时分解键值对
std::map<int, std::string> map{{1, "a"}, {2, "b"}};
for (const auto& [key, val] : map) {
std::cout << key << ":" << val << " "; // 输出:1:a 2:b
}
return 0;
}
3. if/switch 语句初始化
在 if 或 switch 中声明变量,作用域仅限于语句块,避免污染外部作用域。
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> map{{1, "one"}, {2, "two"}};
// if 语句中初始化变量(it 仅在 if 块内可见)
if (auto it = map.find(1); it != map.end()) {
std::cout << "Found: " << it->second << std::endl; // 输出:Found: one
}
// switch 语句中初始化变量
switch (int x = 2; x) { // x 仅在 switch 块内可见
case 1: std::cout << "1"; break;
case 2: std::cout << "2"; break; // 输出:2
}
return 0;
}
4. 内联变量
用 inline 修饰变量,允许在头文件中定义全局变量(避免多文件包含时的重复定义错误)。
// header.h(可被多个文件包含)
#ifndef HEADER_H
#define HEADER_H
inline int global_var = 42; // 内联变量,多文件包含不冲突
#endif
// main.cpp
#include <iostream>
#include "header.h"
int main() {
std::cout << global_var << std::endl; // 输出:42
return 0;
}
5. 折叠表达式
简化变参模板参数包的展开(支持 +、*、&&、|| 等运算符)。
#include <iostream>
// 折叠表达式求和(展开参数包并累加)
template <typename... Args>
auto sum(Args... args) {
return (... + args); // 等价于 (((arg1 + arg2) + arg3) + ...)
}
int main() {
std::cout << sum(1, 2, 3, 4) << std::endl; // 输出:10
std::cout << sum(1.5, 2.5, 3) << std::endl; // 输出:7
return 0;
}
6. constexpr Lambda 表达式
C++17 允许 Lambda 表达式为 constexpr,可在编译期执行。
#include <iostream>
int main() {
// constexpr Lambda:编译期计算
constexpr auto square = [](int x) {
return x * x;
};
constexpr int val = square(5); // 编译期计算:25
std::cout << val << std::endl;
return 0;
}
7. 嵌套命名空间简化
直接用 :: 定义嵌套命名空间(无需多层嵌套 namespace 块)。
#include <iostream>
// 简化的嵌套命名空间(等价于 namespace A { namespace B { namespace C { ... } } })
namespace A::B::C {
void func() {
std::cout << "A::B::C::func()" << std::endl;
}
}
int main() {
A::B::C::func(); // 输出:A::B::C::func()
return 0;
}
8. __has_include 预处理表达式
编译期检查头文件是否存在,用于条件编译。
#include <iostream>
// 检查头文件是否存在
#if __has_include(<optional>)
#include <optional>
#define HAS_OPTIONAL 1
#else
#define HAS_OPTIONAL 0
#endif
int main() {
if (HAS_OPTIONAL) {
std::cout << "optional 头文件存在" << std::endl;
} else {
std::cout << "optional 头文件不存在" << std::endl;
}
return 0;
}
9. Lambda 中用 *this 捕获对象副本
捕获当前对象的副本(而非引用),避免 Lambda 生命周期超过对象时的悬垂引用。
#include <iostream>
#include <memory>
struct MyClass {
int value = 42;
auto get_lambda() {
// 捕获 *this(对象副本)
return [*this]() {
std::cout << value << std::endl; // 使用副本的 value
};
}
};
int main() {
auto lambda = std::make_unique<MyClass>()->get_lambda();
lambda(); // 安全访问副本,输出:42(原对象已销毁,但副本仍有效)
return 0;
}
10. 新增 Attribute
[[nodiscard]]:提示函数返回值不应被忽略(忽略时编译器警告)。[[fallthrough]]:明确标记switch中有意省略break(避免编译器警告)。
#include <iostream>
// [[nodiscard]]:返回值不应被忽略
[[nodiscard]] int calculate() {
return 42;
}
int main() {
calculate(); // 警告:忽略 [[nodiscard]] 函数的返回值
int x = 2;
switch (x) {
case 1: std::cout << "1"; break;
case 2:
std::cout << "2";
[[fallthrough]]; // 明确允许穿透到下一个 case
case 3: std::cout << "3"; break; // 输出:23
}
return 0;
}
11. 字符串转换(std::from_chars/std::to_chars)
高效的字符串与数值转换(无 locale 依赖,性能优于 atoi/to_string)。
#include <iostream>
#include <charconv> // for from_chars/to_chars
#include <array>
int main() {
// 字符串转整数
const char* str = "12345";
int val;
auto [ptr, ec] = std::from_chars(str, str + 5, val);
if (ec == std::errc()) {
std::cout << "转换结果:" << val << std::endl; // 输出:12345
}
// 整数转字符串
std::array<char, 10> buf;
int num = 6789;
auto [end, ec2] = std::to_chars(buf.data(), buf.data() + buf.size(), num);
if (ec2 == std::errc()) {
std::cout << "转换结果:" << std::string(buf.data(), end) << std::endl; // 输出:6789
}
return 0;
}
12. std::variant
类型安全的联合体(可存储多种类型中的一种,需显式指定类型)。
#include <iostream>
#include <variant> // C++17 新增
int main() {
std::variant<int, double, std::string> var;
var = 10; // 存储 int
std::cout << std::get<int>(var) << std::endl; // 输出:10
var = 3.14; // 存储 double
std::cout << std::get<double>(var) << std::endl; // 输出:3.14
var = "hello"; // 存储 string
std::cout << std::get<std::string>(var) << std::endl; // 输出:hello
// 检查当前类型
if (auto* p = std::get_if<std::string>(&var)) {
std::cout << "当前类型是 string:" << *p << std::endl;
}
return 0;
}
13. std::optional
表示 “可能有值或无值” 的对象(用于可能失败的操作返回,避免使用 nullptr 或特殊值)。
#include <iostream>
#include <optional> // C++17 新增
// 可能返回值或无值(如查找失败)
std::optional<int> find_in_vector(const std::vector<int>& vec, int target) {
for (int v : vec) {
if (v == target) return v;
}
return std::nullopt; // 无值
}
int main() {
std::vector<int> vec = {1, 2, 3, 4};
auto result = find_in_vector(vec, 3);
if (result.has_value()) { // 检查是否有值
std::cout << "找到:" << result.value() << std::endl; // 输出:找到:3
}
auto no_result = find_in_vector(vec, 5);
if (!no_result) {
std::cout << "未找到" << std::endl;
}
return 0;
}
14. std::any
可存储任意类型的值(类型擦除,需手动指定类型提取)。
#include <iostream>
#include <any> // C++17 新增
#include <string>
int main() {
std::any a = 10; // 存储 int
std::any b = 3.14; // 存储 double
std::any c = std::string("hello"); // 存储 string
// 提取值(需指定类型)
std::cout << std::any_cast<int>(a) << std::endl; // 输出:10
std::cout << std::any_cast<double>(b) << std::endl; // 输出:3.14
std::cout << std::any_cast<std::string>(c) << std::endl; // 输出:hello
// 检查类型
if (a.type() == typeid(int)) {
std::cout << "a 是 int 类型" << std::endl;
}
return 0;
}
15. std::apply
将 tuple 的元素作为参数传递给函数。
#include <iostream>
#include <tuple>
#include <utility> // for apply
int sum(int a, double b, const std::string& c) {
std::cout << c << ":";
return a + b;
}
int main() {
auto t = std::make_tuple(3, 2.5, "sum");
// 将 tuple 的元素 (3, 2.5, "sum") 作为参数传给 sum
double result = std::apply(sum, t);
std::cout << result << std::endl; // 输出:sum:5.5
return 0;
}
16. std::make_from_tuple
用 tuple 的元素构造对象(参数传递给构造函数)。
#include <iostream>
#include <tuple>
#include <memory> // for make_from_tuple
struct Point {
int x, y;
Point(int a, int b) : x(a), y(b) {
std::cout << "Point 构造:(" << x << "," << y << ")" << std::endl;
}
};
int main() {
auto t = std::make_tuple(10, 20); // tuple 元素作为构造参数
Point p = std::make_from_tuple<Point>(t); // 用 tuple 构造 Point
return 0; // 输出:Point 构造:(10,20)
}
17. std::as_const
将对象转换为 const 引用(避免意外修改,简化代码)。
#include <iostream>
#include <vector>
#include <utility> // for as_const
int main() {
std::vector<int> vec = {1, 2, 3};
// 普通遍历(可修改元素)
for (auto& v : vec) { v *= 2; }
// 用 as_const 转为 const 引用(仅读取)
for (const auto& v : std::as_const(vec)) {
std::cout << v << " "; // 输出:2 4 6
}
return 0;
}
18. std::string_view
轻量级字符串视图(引用现有字符串,不复制数据,适合只读场景)。
#include <iostream>
#include <string_view> // C++17 新增
// 接受 string_view 而非 string,避免复制
void print_str(std::string_view sv) {
std::cout << sv << std::endl;
}
int main() {
std::string s = "Hello World";
print_str(s); // 传递 string(隐式转换为 string_view)
print_str("Hello C++17"); // 传递字符串字面量(无复制)
print_str(s.substr(0, 5)); // 传递子串(无复制)
return 0;
}
19. std::filesystem
标准化的文件系统操作库(创建目录、遍历文件等)。
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem; // 简化命名空间
int main() {
// 创建目录
fs::create_directory("test_dir");
// 遍历目录
for (const auto& entry : fs::directory_iterator(".")) {
std::cout << entry.path() << std::endl; // 输出当前目录下的文件/目录
}
// 检查路径是否存在
if (fs::exists("test_dir")) {
fs::remove("test_dir"); // 删除目录
}
return 0;
}
20. std::shared_mutex
C++17 简化的共享互斥锁(替代 C++14 的 std::shared_timed_mutex,无超时机制)。
#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>
std::shared_mutex mtx; // C++17 共享互斥锁
int data = 0;
void reader(int id) {
std::shared_lock<std::shared_mutex> lock(mtx); // 共享锁(读)
std::cout << "Reader " << id << ": " << data << std::endl;
}
void writer() {
std::unique_lock<std::shared_mutex> lock(mtx); // 独占锁(写)
data++;
std::cout << "Writer: " << data << std::endl;
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 3; ++i) threads.emplace_back(reader, i);
threads.emplace_back(writer);
for (int i = 3; i < 6; ++i) threads.emplace_back(reader, i);
for (auto& t : threads) t.join();
return 0;
}
四、C++20 新增特性及代码示例
1. 模块(Modules)
替代传统头文件(#include),减少编译时间,控制接口暴露。
// math_module.cppm(模块文件)
export module math; // 定义模块
export int add(int a, int b) { // 导出函数
return a + b;
}
int multiply(int a, int b) { // 未导出(模块内私有)
return a * b;
}
// main.cpp
import math; // 导入模块
#include <iostream>
int main() {
std::cout << add(2, 3) << std::endl; // 输出:5(可访问导出函数)
// std::cout << multiply(2, 3) << std::endl; // 错误:multiply 未导出
return 0;
}
2. 范围库(Ranges)
简化算法操作(支持链式调用,自动处理迭代器)。
#include <iostream>
#include <vector>
#include <ranges> // C++20 范围库
#include <algorithm>
int main() {
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// 链式操作:筛选偶数 → 乘以 2 → 输出
auto result = nums | std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * 2; });
for (int x : result) {
std::cout << x << " "; // 输出:4 8 12 16
}
return 0;
}
3. 概念库(Concepts)
约束模板参数类型(替代繁琐的 enable_if,编译错误更友好)。
#include <iostream>
#include <concepts> // C++20 概念库
// 约束 T 必须是整数类型(integral 是预定义概念)
template <std::integral T> // 等价于 requires std::is_integral_v<T>
T square(T x) {
return x * x;
}
int main() {
std::cout << square(5) << std::endl; // 正确:int 是整数类型 → 25
// std::cout << square(3.14) << std::endl; // 错误:double 不是整数类型
return 0;
}
4. 协程(Coroutines)
用于异步操作(可暂停 / 恢复,避免回调地狱),需配合 std::generator(C++20 技术规范)。
#include <iostream>
#include <generator> // C++20 协程生成器
// 协程:生成从 start 到 end 的序列
std::generator<int> range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i; // 暂停并返回当前值
}
}
int main() {
for (int x : range(1, 5)) { // 遍历协程生成的序列
std::cout << x << " "; // 输出:1 2 3 4
}
return 0;
}
5. 并发库(std::jthread)
std::jthread 是 std::thread 的改进版,析构时自动 join(避免忘记 join 导致的程序崩溃)。
#include <iostream>
#include <thread> // C++20 jthread
void task(int id) {
std::cout << "Thread " << id << " running" << std::endl;
}
int main() {
{
std::jthread t1(task, 1); // 自动 join(离开作用域时析构)
std::jthread t2(task, 2);
} // t1、t2 在此处自动 join
std::cout << "All threads joined" << std::endl;
return 0;
}
6. 同步库(std::latch)
协调多个线程(等待所有线程完成后再继续)。
#include <iostream>
#include <thread>
#include <latch> // C++20 同步库
void worker(int id, std::latch& latch) {
std::cout << "Worker " << id << " done" << std::endl;
latch.count_down(); // 计数减 1
}
int main() {
const int num_workers = 3;
std::latch latch(num_workers); // 初始计数为 3
std::jthread t1(worker, 1, std::ref(latch));
std::jthread t2(worker, 2, std::ref(latch));
std::jthread t3(worker, 3, std::ref(latch));
latch.wait(); // 等待计数减为 0(所有工人完成)
std::cout << "All workers done" << std::endl;
return 0;
}
7. Lambda 表达式的更新(模板 Lambda)
C++20 允许 Lambda 显式声明模板参数(更灵活的泛型)。
#include <iostream>
#include <string>
int main() {
// 模板 Lambda(显式指定模板参数)
auto print = []<typename T>(const T& value) {
std::cout << value << std::endl;
};
print(42); // T 推导为 int → 输出:42
print(3.14); // T 推导为 double → 输出:3.14
print("hello"); // T 推导为 const char* → 输出:hello
return 0;
}
8. 指定初始化(Designated Initializers)
按成员名初始化结构体 / 数组(无需按声明顺序)。
#include <iostream>
struct Point {
int x;
int y;
};
int main() {
// 按成员名初始化结构体(C++20 支持)
Point p = {.y = 20, .x = 10}; // 不按声明顺序,通过成员名指定
std::cout << "x=" << p.x << ", y=" << p.y << std::endl; // 输出:x=10, y=20
// 数组指定初始化
int arr[5] = {[2] = 10, [0] = 5}; // 索引 2 为 10,索引 0 为 5
for (int i = 0; i < 5; ++i) {
std::cout << arr[i] << " "; // 输出:5 0 10 0 0
}
return 0;
}
9. 船型操作符 <=>(三路比较运算符)
自动生成所有比较运算符(==、!=、<、>、<=、>=)。
#include <iostream>
struct Person {
std::string name;
int age;
// 三路比较:按 age 比较,自动生成所有比较运算符
auto operator<=>(const Person& other) const {
return age <=> other.age;
}
};
int main() {
Person a{"Alice", 25};
Person b{"Bob", 30};
std::cout << (a < b) << std::endl; // 输出:1(true,25 < 30)
std::cout << (a > b) << std::endl; // 输出:0(false)
std::cout << (a <= b) << std::endl; // 输出:1(true)
return 0;
}
10. 范围 for 循环支持初始化语句
在范围 for 循环中声明变量(作用域仅限于循环)。
#include <iostream>
#include <vector>
// 返回一个临时 vector
std::vector<int> get_numbers() {
return {1, 2, 3, 4};
}
int main() {
// 循环中初始化变量(避免污染外部作用域)
for (auto nums = get_numbers(); int x : nums) {
std::cout << x << " "; // 输出:1 2 3 4
}
return 0;
}
11. 非类型模板形参支持字符串
C++20 允许字符串作为非类型模板参数(需为 constexpr 字符串)。
#include <iostream>
// 字符串作为非类型模板参数
template <const char* Str>
struct Message {
static void print() {
std::cout << Str << std::endl;
}
};
constexpr char hello[] = "Hello C++20"; // constexpr 字符串
int main() {
Message<hello>::print(); // 输出:Hello C++20
return 0;
}
12. C++ 属性符([[likely]]/[[unlikely]])
提示编译器分支的可能性,优化代码生成。
#include <iostream>
int main() {
int x = 1;
if (x == 1) [[likely]] { // 提示该分支更可能执行
std::cout << "x is 1" << std::endl;
} else [[unlikely]] { // 提示该分支很少执行
std::cout << "x is not 1" << std::endl;
}
return 0;
}
13. 日历功能(std::chrono::year_month_day)
处理日期(年、月、日)的标准化库。
#include <iostream>
#include <chrono> // C++20 日历库
int main() {
using namespace std::chrono;
// 构造日期:2024年3月15日
year_month_day ymd{year{2024}, month{3}, day{15}};
std::cout << ymd.year() << "-" << ymd.month() << "-" << ymd.day() << std::endl;
// 输出:2024-Mar-15
return 0;
}
14. 时区功能(std::chrono::zoned_time)
处理带时区的时间(如 UTC 与本地时间转换)。
#include <iostream>
#include <chrono> // C++20 时区库
int main() {
using namespace std::chrono;
// 当前 UTC 时间
auto now_utc = system_clock::now();
zoned_time zt{"UTC", now_utc};
std::cout << "UTC 时间:" << zt << std::endl;
// 转换为纽约时区
zoned_time ny_zt{"America/New_York", now_utc};
std::cout << "纽约时间:" << ny_zt << std::endl;
return 0;
}
15. std::span
连续序列的视图(类似 string_view,但适用于任意类型数组 / 容器)。
#include <iostream>
#include <span> // C++20 新增
#include <vector>
// 接受 span 而非容器,支持任意连续序列(数组、vector 等)
void print_span(std::span<int> s) {
for (int x : s) {
std::cout << x << " ";
}
std::cout << std::endl;
}
int main() {
int arr[] = {1, 2, 3};
std::vector<int> vec = {4, 5, 6};
print_span(arr); // 传递数组 → 输出:1 2 3
print_span(vec); // 传递 vector → 输出:4 5 6
print_span(vec.data() + 1, 2); // 传递子序列 → 输出:5 6
return 0;
}
16. 特性测试宏
编译期检查编译器是否支持特定 C++ 特性(如 __cpp_concepts 检查是否支持 concepts)。
#include <iostream>
int main() {
#if __cpp_concepts >= 201907L // 检查 concepts 支持
std::cout << "支持 C++20 concepts" << std::endl;
#else
std::cout << "不支持 C++20 concepts" << std::endl;
#endif
#if __cpp_ranges >= 201911L // 检查 ranges 支持
std::cout << "支持 C++20 ranges" << std::endl;
#else
std::cout << "不支持 C++20 ranges" << std::endl;
#endif
return 0;
}
17. constexpr 的更新
C++20 允许 constexpr 函数使用动态内存分配、try/catch 等(进一步扩展编译期计算能力)。
#include <iostream>
#include <vector>
// constexpr 函数中使用动态内存(vector 内部有动态分配)
constexpr int sum(const std::vector<int>& vec) {
int s = 0;
for (int v : vec) s += v;
return s;
}
int main() {
constexpr std::vector<int> vec = {1, 2, 3, 4}; // constexpr vector(C++20 支持)
constexpr int total = sum(vec); // 编译期计算:10
std::cout << total << std::endl;
return 0;
}
18. consteval 函数
强制在编译期执行的函数(比 constexpr 更严格,不允许运行期执行)。
#include <iostream>
// consteval:必须在编译期执行
consteval int square(int x) {
return x * x;
}
int main() {
constexpr int val1 = square(5); // 正确:编译期执行 → 25
// int x = 5;
// int val2 = square(x); // 错误:x 是运行期变量,无法编译期执行
std::cout << val1 << std::endl;
return 0;
}
19. constinit
确保变量在常量初始化阶段初始化(避免静态初始化顺序问题)。
#include <iostream>
// 常量初始化(编译期完成)
constinit int global_var = 42;
int main() {
std::cout << global_var << std::endl; // 输出:42
return 0;
}
20. 用 using 引用 enum 类型
简化 enum 成员的访问(无需重复 enum 名)。
#include <iostream>
enum class Color {
Red, Green, Blue
};
int main() {
using Color::Red; // 引用 enum 成员
using Color::Green;
Color c = Red; // 无需写 Color::Red
if (c == Green) { // 直接使用 Green
std::cout << "Green" << std::endl;
} else {
std::cout << "Red" << std::endl; // 输出:Red
}
return 0;
}
21. 格式化库(std::format)
类型安全的字符串格式化(类似 printf,但支持任意类型,编译期检查格式)。
#include <iostream>
#include <format> // C++20 格式化库
int main() {
std::string s = std::format("Hello, {}! Age: {}", "Alice", 30);
std::cout << s << std::endl; // 输出:Hello, Alice! Age: 30
// 格式化数字
std::cout << std::format("Pi: {:.2f}", 3.14159) << std::endl; // 输出:Pi: 3.14
return 0;
}
22. 数学常量(std::numbers)
标准化的数学常量(如 π、e 等)。
#include <iostream>
#include <numbers> // C++20 数学常量
int main() {
std::cout << "π = " << std::numbers::pi << std::endl; // 输出:3.14159...
std::cout << "e = " << std::numbers::e << std::endl; // 输出:2.71828...
std::cout << "√2 = " << std::numbers::sqrt2 << std::endl; // 输出:1.41421...
return 0;
}
23. std::source_location
获取代码位置信息(文件名、行号、函数名),用于日志或调试。
#include <iostream>
#include <source_location> // C++20 新增
// 获取调用点的位置信息
void log(const std::string& msg,
const std::source_location loc = std::source_location::current()) {
std::cout << "[" << loc.file_name() << ":" << loc.line() << "] " << msg << std::endl;
}
int main() {
log("程序启动"); // 输出:[main.cpp:11] 程序启动
return 0;
}
24. 位运算(std::rotl/std::rotr)
标准化的位旋转操作(循环左移 / 右移)。
#include <iostream>
#include <bit> // C++20 位运算库
int main() {
unsigned int x = 0b1001; // 二进制 9
// 循环左移 1 位:1001 → 0011(左移后高位补低位)
unsigned int rotl = std::rotl(x, 1);
std::cout << std::hex << rotl << std::endl; // 输出:3(0b0011)
// 循环右移 1 位:1001 → 1100(右移后低位补高位)
unsigned int rotr = std::rotr(x, 1);
std::cout << rotr << std::endl; // 输出:c(0b1100)
return 0;
}

2762

被折叠的 条评论
为什么被折叠?



