现代C++编程:C++ 20新特性总结
概念是一种用于描述模板参数必须满足的条件或特性的机制。它们可以看作是模板参数的“类型约束”,让编译器在模板实例化时能够提前验证这些约束,从而提供更明确的错误提示和更强的编译时保障。协程是通过在函数返回类型前面添加co_awaitco_yield或co_return关键字来定义的。// 对范围进行一系列操作: 过滤、变换、排序和反转}) // 只保留偶数}) // 每个数值乘以3// 反转顺序//
C++20 是 C++ 标准中的一个重要版本,引入了许多新特性和改进,显著提升了语言的功能性和易用性。
新关键字
1. co_await
:
co_await 关键字用于暂停当前协程,直到被 awaitable 对象的操作完成。
std::future<int> async_computation() {
co_await std::suspend_never{};
co_return 42;
}
2. co_return
:
用于在协程中返回一个值,并终止协程。
std::future<int> async_computation() {
co_return 42;
}
3. co_yield
:
co_yield
可以用于编写生成器,这种生成器可以在需要时一次生成一个值,并通过 co_yield
将该值返回给调用者。
Generator<int> sequence() {
for (int i = 0; i < 10; ++i) {
co_yield i;
}
}
4. concept
:
关键字 concept
用于定义概念,它是一组对类型进行约束的条件。
template<typename T>
concept Incrementable = requires(T t) {
++t;
t++;
};
约束表达式
约束表达式是定义概念的核心,其形式为带有 requires
关键字的布尔表达式。
5. requires
:
用于定义约束表达式(constraints expressions)。requires
表达式允许我们在模板定义中指定特定的约束条件,从而限制模板参数的类型。该表达式在某些条件满足时为true
,否则为false
。
template<typename T>
requires std::integral<T>
T add(T a, T b) {
return a + b;
}
6. consteval
:
用于声明必须在编译时求值的常量表达式函数。consteval
函数只能被编译时常量上下文调用,如果在运行时调用这些函数,编译器将会报错。
consteval int square(int n) {
return n * n;
}
7. constinit
:
用于声明变量在运行时具有静态存储持续时间且必须被立即初始化。这可以防止未初始化的静态变量带来的问题,同时防止变量没有在编译时就初始化的情况,确保了变量的初始化顺序。
constinit int value = 10;
改进的关键字用法
1. constexpr
:
C++20 扩展了 constexpr
的能力,使得其可以用于更加复杂的表达式和函数。
constexpr
虚函数:虚函数可以是constexpr
。constexpr
动态分配:在constexpr
上下文中支持动态内存分配。constexpr
try-catch:在constexpr
函数中支持try-catch
异常处理。constexpr
构造函数:构造函数可以是constexpr
,甚至支持更加复杂的初始化逻辑。
constexpr 动态分配
#include <iostream>
#include <memory>
constexpr int* createArray(int n) {
int* arr = new int[n];
for (int i = 0; i < n; ++i) {
arr[i] = i * i;
}
return arr;
}
int main() {
constexpr int size = 5;
constexpr int* arr = createArray(size);
for (int i = 0; i < size; ++i) {
std::cout << arr[i] << std::endl; // 输出各个元素的平方
}
delete[] arr; // 清理动态分配的内存
return 0;
}
constexpr try-catch
#include <iostream>
constexpr int divide(int a, int b) {
if (b == 0) {
throw "Division by zero!";
}
return a / b;
}
int main() {
try {
constexpr int result = divide(10, 2);
std::cout << "Result: " << result << std::endl;
} catch (const char* msg) {
std::cerr << msg << std::endl;
}
return 0;
}
constexpr 构造函数
#include <iostream>
class Point {
public:
constexpr Point(int x, int y) : x_(x), y_(y) {}
constexpr int getX() const { return x_; }
constexpr int getY() const { return y_; }
private:
int x_;
int y_;
};
int main() {
constexpr Point p(3, 4);
constexpr int x = p.getX();
constexpr int y = p.getY();
std::cout << "Point: (" << x << ", " << y << ")" << std::endl; // Point: (3, 4)
return 0;
}
2. inline
:
C++17 引入了 inline
变量,C++20 延续了这一特性。inline
变量允许在多个翻译单元(translation units)中定义同一变量,而不会导致链接错误。这在全局变量和静态成员变量中尤其有用。
// header.h
#ifndef HEADER_H
#define HEADER_H
inline int count = 0; // 允许在多个翻译单元中定义
#endif // HEADER_H
// main.cpp
#include "header.h"
#include <iostream>
void increment() {
++count;
}
int main() {
increment();
std::cout << "Count: " << count << std::endl; // Count: 1
return 0;
}
// other.cpp
#include "header.h"
void reset() {
count = 0;
}
3. decltype(auto)
:
用于函数返回类型,推断返回值的精确类型。
auto create_array() -> decltype(auto) {
return std::array<int, 3>{1, 2, 3};
}
其他改进
三路比较运算符 (<=>):
C++20 引入了“三路比较运算符”(<=>),也被称为“太空飞船操作符”,用于实现统一的强比较运算符接口。这使得比较操作更加简洁和一致,更容易使用标准库中的排序和查找函数。
基本概念
三路比较运算符 <=>
生成一个 std::strong_ordering
、std::weak_ordering
、std::partial_ordering
或 std::string
类型的对象,这些对象表示比较的结果:
- 小于 (
<
)(结果为负值表示左操作数小于右操作数) - 等于 (
==
)(结果为零表示左操作数等于右操作数) - 大于 (
>
)(结果为正值表示左操作数大于右操作数)
std::compare_*
是用于进行三路比较的命名空间中的类型。这里是一些常用的:
std::strong_ordering
std::weak_ordering
std::partial_ordering
default
和 delete
:
现在可以在条件编译中使用。
struct MyStruct {
MyStruct() = delete; // 禁止默认构造函数
MyStruct(int) = default; // 使用编译器生成的构造函数
};
————————————————————————————————
概念(Concepts):
C++20中引入了一个非常强大的新特性——概念(Concepts)。概念主要用于约束模板参数,从而使得模板编程更加简洁、直观和安全。它们可以显著提升模板代码的可读性和调试体验。
什么是概念?
概念是一种用于描述模板参数必须满足的条件或特性的机制。它们可以看作是模板参数的“类型约束”,让编译器在模板实例化时能够提前验证这些约束,从而提供更明确的错误提示和更强的编译时保障。
概念的使用场景
- 类型约束:确保模板参数满足某种类型特性,例如可以被复制、可以进行加法运算等。
- 代码优化:通过限制模板参数的类型特性,可以生成更优化的代码。
- 错误提示:在模板实例化时,如果参数不符合概念约束,可以提供更具描述性的错误信息。
定义和使用:使用 concept 关键字定义概念,使用 requires 子句或直接在模板参数列表中约束模板参数。
————————————————————————————————
协程(Coroutines):
C++20 引入了协程(Coroutines)这一特性,极大地简化了异步编程和生成器的实现。协程允许函数在执行过程中挂起,然后在稍后的某个时间点恢复执行,而不会阻塞线程。这一特性对于实现高性能的异步I/O操作、事件驱动编程和复杂的控制流都非常有用。
协程的基本概念
协程是一种可以在执行过程中多次暂停和恢复的函数,与普通的函数(只能一次调用到结束)不同。它们提供了一种更灵活的控制流机制。
协程的核心元素
C++20 中的协程是通过以下几个核心概念来实现的:
- 协程句柄(Coroutine Handle):用于控制和操作协程的对象。
- 协程承诺类型(Promise Type):描述协程的状态和行为。
- 协程返回对象(Coroutine Return Object):协程函数返回的对象,用于控制协程。
协程句柄
协程句柄在C++20的协程框架中扮演了非常重要的角色。它是一个指向协程状态的指针,可以用来控制协程的执行。协程句柄使得开发者能够挂起、恢复和销毁协程,同时也可以访问协程的内部状态和数据。
协程句柄的作用
-
控制协程的执行
协程句柄主要用于暂停和恢复协程的执行。通过调用resume()
,你可以恢复协程的执行,当协程需要挂起时,它会自动挂起,停止执行,直到再次调用resume()
。 -
销毁协程
当协程不再需要时,可以调用handle_.destroy()
来销毁协程,释放占用的资源。 -
检查协程状态
你可以通过协程句柄检查协程是否已经完成。done()
方法可以用于判断协程是否已经执行完毕。 -
访问Promise对象
协程句柄提供了访问 promise
对象的能力,这对于获取协程的当前状态或数值非常有用。
协程承诺类型
C++20协程的承诺类型定义了协程的行为、状态管理和与外部交互的机制。承诺类型包含了控制协程开始、挂起、恢复和结束的各种操作。通过承诺类型,开发者可以定制协程的整个生命周期,包括如何处理生成的值、异常以及协程何时执行。
承诺类型的基本概念
协程承诺类型就是一个类或结构体,它需要实现一些特定的成员函数。这些成员函数定义了协程在不同阶段的行为,例如初始化、挂起、恢复和结束等。
构建承诺类型需要的成员函数
一下是构建承诺类型所需实现的关键成员函数:
1. get_return_object
这个函数用于返回协程的控制对象(通常是协程返回类型的实例)。这个控制对象能被用来与协程进行交互,如启动、暂停、恢复和销毁协程。
2. initial_suspend
这个函数定义了协程启动时是否挂起。它返回一个 suspend_always
表示协程一启动就挂起,返回 suspend_never
表示协程启动后直接执行。
3. final_suspend
这个函数定义了协程结束时的行为。通常返回一个 suspend_always
表示协程结束时挂起,可以在此时清理资源,或返回 suspend_never
表示协程结束时不挂起。
4. return_void
或 return_value
如果协程不需要生成特定的返回值,实现 return_void
;如果需要返回值,实现 return_value
。
5. unhandled_exception
当协程抛出未处理的异常时调用。通常在这里处理异常,进行清理工作。
6. yield_value
用于定义在协程中生成值的行为。通常与 co_yield
关键字配合使用。
协程返回对象
C++20 协程返回对象是协程函数返回的对象,它由协程的承诺类型(Promise Type)负责创建,并且该对象是与外部代码交互的主要接口。返回对象允许调用方控制协程的执行行为,例如启动、挂起、恢复和销毁协程。
返回对象的核心作用
- 控制协程生命周期:包括启动、暂停、恢复和终止协程。
- 访问协程的返回值或生成值:通过返回对象,外部调用可以获取协程生成的值或返回的结果。
- 管理协程状态:可以查询协程是否完成、是否挂起等状态信息。
构建返回对象
返回对象通常是由协程的承诺类型的 get_return_object()
方法返回的自定义类型。这个自定义类型需要封装一个协程句柄,并通过协程句柄来控制协程的执行和状态访问。
使用协程的基本步骤
1. 定义一个协程
协程是通过在函数返回类型前面添加 co_await
、co_yield
或 co_return
关键字来定义的。
2. 协程的状态管理
协程状态的管理主要通过promise_type来实现,该类型需要实现一些特定的成员函数,例如 initial_suspend, final_suspend, return_void, 以及 yield_value 等。
3. 协程的句柄控制
协程的执行、挂起和恢复通过句柄控制(std::coroutine_handle)。协程的句柄可以用于恢复协程、检查协程状态以及销毁协程等。
————————————————————————————————
模块(Modules):
C++20引入模块(Modules)作为一种替代传统头文件的机制,旨在提高C++项目的编译速度、代码组织和模块化。模块减少了编译时间依赖性和复杂性,提供了更好的编译单元分离方式。
模块的核心概念
- 模块声明(Module Declarations):定义模块的开始。
- 导出声明(Export Declarations):声明哪些符号(如类、函数、变量)可以被其他模块使用。
- 导入声明(Import Declarations):引入其他模块的符号。
C++20 模块(Modules)是一种用来替代传统头文件的机制。模块的声明通过以下步骤组织代码,更加高效且易于维护。模块声明的一部分称作“模块声明部分”,用于定义模块的名称并包含导入和导出声明。
模块声明部分
模块声明部分位于模块实现文件的开头,用于声明模块名和模块导出内容。主要语法包括:
1. 模块声明
使用 module
关键字声明模块的名称。
module ModuleName;
2. 模块分块声明
模块可以分成多个部分,通过子模块分块声明。
module ModuleName:PartName;
3. 导出声明
使用 export 关键字导出模块的符号(函数、类、变量等),从而可以被其他模块或翻译单元使用。
export int add(int a, int b);
————————————————————————————————
范围(Ranges):
C++20 引入了范围(Ranges)库,它提供了一种现代、简洁的方式来处理序列(如数组、容器等)上的操作。范围库的主要特点是允许通过组合函数和适配器链接操作,使代码更直观和易读。范围库包含在 <ranges>
头文件中。
核心概念
- 范围(Range):可以被遍历的序列,如数组、向量、链表等。
- 适配器(Views):非侵入性地转换或筛选范围,不改变底层容器的数据。
- 操作(Actions):对范围进行处理的函数,如过滤、变换、排序等。
基本用法
引用范围库
首先,需要包含范围库的头文件:
#include <ranges>
#include <vector>
#include <iostream>
过滤和变换
范围库使得数据的过滤和变换变得更加简单。
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 范围过滤:只留下偶数
auto even_numbers = numbers | std::ranges::views::filter([](int n) { return n % 2 == 0; });
// 范围变换:将每个数值乘以2
auto doubled_numbers = even_numbers | std::ranges::views::transform([](int n) { return n * 2; });
// 打印结果
for (int n : doubled_numbers) {
std::cout << n << " ";
}
return 0;
}
这个示例过滤了偶数,然后将偶数翻倍并打印结果。输出为:
4 8 12 16 20
常用的范围适配器和操作
1. filter
filter
用于筛选满足特定条件的元素。
auto even_numbers = numbers | std::ranges::views::filter([](int n) { return n % 2 == 0; });
2. transform
transform
用于将每个元素转换为新值。
auto squared_numbers = numbers | std::ranges::views::transform([](int n) { return n * n; });
3. take
take
用于获取某个范围内的前 n
个元素。
auto first_five = numbers | std::ranges::views::take(5);
4. drop
drop
用于丢弃某个范围内的前 n
个元素。
auto without_first_two = numbers | std::ranges::views::drop(2);
5. reverse
reverse
用于反转范围内元素的顺序。
auto reversed_numbers = numbers | std::ranges::views::reverse;
6. sort
sort
用于对范围内的元素进行排序。
#include <algorithm> // 需要包含 <algorithm> 头文件
std::ranges::sort(numbers);
示例总结
汇总前面的各个适配器,展示一个更复杂的示例:
#include <ranges>
#include <vector>
#include <iostream>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 3, 5, 2, 4, 6, 9, 7, 8, 10};
// 对范围进行一系列操作: 过滤、变换、排序和反转
auto result = numbers
| std::ranges::views::filter([](int n) { return n % 2 == 0; }) // 只保留偶数
| std::ranges::views::transform([](int n) { return n * 3; }) // 每个数值乘以3
| std::ranges::views::reverse; // 反转顺序
// 打印结果
for (int n : result) {
std::cout << n << " ";
}
return 0;
}
编译和运行
确保使用支持 C++20 的编译器,例如 g++
:
g++ -std=c++20 -o ranges_example ranges_example.cpp
./ranges_example
范围库的优点
- 可读性:链式语法使代码更加直观和易读。
- 灵活性:通过组合不同的适配器和操作,可以轻松实现复杂的数据处理任务。
- 性能:范围库使用懒评估(lazy evaluation),避免不必要的中间结果计算,提高性能。
————————————————————————————————
Lambda 改进:
C++20 对 Lambda 表达式做了不少改进,使其更强大和灵活。以下是 C++20 对 Lambda 表达式的一些主要改进:
1. Lambda 默认构造函数
在 C++14 和 C++17 中,Lambda 表达式生成的闭包类型默认是不可默认构造的。C++20 允许 Lambda 表达式生成的闭包类型拥有默认构造函数,使得 Lambda 可以在某些情况下被默认构造。
2. 模板 Lambda
C++20 允许 Lambda 表达式直接使用模板参数,使得 Lambda 泛型编程更为自然和方便。
3. 捕获包扩展
C++20 允许捕获包扩展(Pack Expansion in Lambda Capture),使得可以捕获多参数包到 Lambda 捕获列表。
4. constexpr
Lambda
C++20 进一步增强了对 Lambda 的 constexpr
支持。在 C++20 中,你可以编写 constexpr
Lambda 函数,使其表达式能在编译时计算。
5. 使用 template
关键字的 Lambda
C++20 在 Lambda 表达式中允许使用 template
关键字定义模板参数。
6. 捕获 this
的移动对象
C++20 允许在 Lambda 表达式中通过 [obj = std::move(*this)]
的方式来捕获当前对象,从而实现移动捕获。
7. 更强大的隐式捕获
在 C++20 中,Lambda 表达式支持捕获外部对象的一部分,并且能通过编译时明确捕获其值或引用。
————————————————————————————————
using
扩展:
C++20 对 using
进行了扩展,使其在类型别名和模板定义中更加灵活。
1. using
用于枚举
C++20 允许在 using
语句中引用枚举类型的底层类型。通过这种方式,可以简化对枚举类型的使用,特别是在模板和泛型编程中。
2. using
用于别名模板
C++11 已经引入了别名模板(alias templates),C++20 允许更灵活地使用 using
定义模板别名,使得代码更加简洁和易读。
3. using
声明变量
C++20 简化了 using
的使用,允许在局部作用域中使用 using
声明变量。这种方式结合了类型别名和 auto
的优势。
4. using
用于非类型模板参数
C++20 引入了允许 using
与非类型模板参数一起使用的新特性,这使得模板代码更加简洁和易读。
5. 使用 using
定义模板别名作用域
C++20 允许更灵活地在不同作用域中使用 using
语句,以定义模板别名。这种方式使得命名空间和类作用域中的模板别名声明更加清晰。
6. 在局部作用域中使用 using
类型别名和变量声明
C++20 允许在局部作用域中结合 using
声明变量和类型别名,对于自动类型推导和模板编程更加灵活。
————————————————————————————————
immediate functions
:
C++20 引入了即时函数(Immediate Functions)的概念,通过使用关键字 consteval
将其定义为显式常量求值函数。即时函数在编译期进行求值,与 constexpr
相比,consteval
的函数必须在编译期求值,若在运行期调用则会导致编译错误。
关键特性
- 编译期求值:即时函数只能在编译期进行求值。
- 严格常量求值:即时函数必须在编译期被求值,不能在运行期调用。
- 显式约束:使用
consteval
关键字显式声明,使得开发者明确知道该函数必须在编译期求值。
基本语法
consteval int compute_square(int n) {
return n * n;
}
————————————————————————————————————————————
新的 char8_t
类型:
C++20 引入了 char8_t
类型,这是用来处理 UTF-8 编码的字符类型。之前,UTF-8 编码通常使用 char
类型来表示,但这会导致类型混淆和潜在的错误。char8_t
提供了更为明确的方式来处理 UTF-8 编码的字符。
主要特性
- 强类型区分:
char8_t
是一个新的基础类型,用于明确区分 UTF-8 编码的字符和其他字符类型。 - 提高代码安全性:通过显式类型,减少了编码转换中的错误和不兼容问题。
- 标准库支持:C++20 标准库引入了一些函数和模板对
char8_t
类型的支持,有助于处理字符串操作。
基本语法
char8_t utf8_char = u8'a'; // 单个 UTF-8 编码字符
u8"Hello, UTF-8!" // UTF-8 编码的字符串字面量
———————————————————————————————————————————
标准库特性
1. 格式化库(std::format
):
C++20 引入了 std::format
库,提供了一种功能强大且灵活的字符串格式化方式。与传统的 printf
和 std::stringstream
方法相比,std::format
更加简洁且安全,支持类型安全的格式化,并且可以更方便地与现代 C++ 标准库集成。
主要特性
- 类型安全:
std::format
提供了类型安全的字符串格式化,避免了类型不匹配的错误。 - 简洁灵活:格式化语法简洁且强大,支持多种数据类型和格式选项。
- 扩展性强:支持用户自定义格式化,通过特化
std::formatter
模板来定义新的格式化行为。
基本语法
std::format
的基本使用方法如下:
#include <iostream>
#include <format>
int main() {
int num = 42;
std::string formatted_str = std::format("The answer is: {}", num);
std::cout << formatted_str << std::endl; // 输出: The answer is: 42
return 0;
}
常用格式化选项
1. 基本占位符
占位符 {}
可以用于格式化传入的任意类型的参数。
#include <iostream>
#include <format>
int main() {
std::string name = "Alice";
int age = 30;
double pi = 3.14159;
std::cout << std::format("Name: {}, Age: {}\n", name, age);
std::cout << std::format("Pi: {:.2f}\n", pi); // 输出到小数点后两位
return 0;
}
2. 指定宽度和填充字符
可以指定宽度和填充字符,以确保格式化后的字符串具有特定的长度。
#include <iostream>
#include <format>
int main() {
int value = 42;
std::cout << std::format("{:>10}\n", value); // 右对齐,宽度为10
std::cout << std::format("{:<10}\n", value); // 左对齐,宽度为10
std::cout << std::format("{:0>10}\n", value); // 用0填充,宽度为10
return 0;
}
3. 使用不同的进制表示数值
可以使用不同的格式化选项来表示数值,例如二进制、八进制和十六进制。
#include <iostream>
#include <format>
int main() {
int number = 255;
std::cout << std::format("Decimal: {}\n", number);
std::cout << std::format("Hexadecimal: {:#x}\n", number); // 带有前缀0x
std::cout << std::format("Octal: {:#o}\n", number); // 带有前缀0
std::cout << std::format("Binary: {:#b}\n", number); // 带有前缀0b
return 0;
}
用户自定义类型的格式化
通过特化 std::formatter
模板,可以为用户自定义类型提供格式化支持。
#include <iostream>
#include <format>
struct Point {
int x, y;
};
template <>
struct std::formatter<Point> {
constexpr auto parse(std::format_parse_context& ctx) { return ctx.end(); }
auto format(const Point& p, std::format_context& ctx) {
return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
}
};
int main() {
Point pt{3, 4};
std::cout << std::format("Point: {}\n", pt);
return 0;
}
————————————————————————————————————————
2. 协程支持:
在C++20中,标准库对协程(coroutines)的支持大大增强。协程是一种特殊的函数,它可以在执行过程中暂停并在稍后恢复。C++20标准库引入了一些新的类型和函数来支持协程,以下是一些关键的类型:
-
std::coroutine_handle
:
这是一个泛化的句柄类型,用于操作协程。#include <coroutine> template <typename PromiseType = void> struct coroutine_handle { // Creates an empty coroutine handle. static coroutine_handle from_address(void *addr) noexcept; // Other member functions... };
-
std::suspend_always
:
这是一个可悬挂对象,总是悬挂协程。#include <coroutine> struct suspend_always { bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<>) const noexcept {} void await_resume() const noexcept {} };
-
std::suspend_never
:
这是一个可悬挂对象,从不悬挂协程。#include <coroutine> struct suspend_never { bool await_ready() const noexcept { return true; } void await_suspend(std::coroutine_handle<>) const noexcept {} void await_resume() const noexcept {} };
3. std::span
:
std::span
是 C++20 标准库中引入的一种视图类型,用于在不复制数据的情况下提供对数组或容器的一段连续元素的访问。它类似于指针,但比指针更安全、更便捷。std::span
提供了一种轻量级的方式来引用现有的数据,不拥有数据,也不管理其生命周期。
std::span
的特性
- 轻量级:不包含任何数据,只是对现有数据的引用。
- 不可变:尺寸和元素类型在创建后不可更改。
- 安全:在使用
span
时,编译器可以检查边界。
————————————————————————————————————————————
4. std::jthread
:
std::jthread
是 C++20 引入的一个新特性,它提供了一个管理线程的方式,与之前版本的 std::thread
相比,std::jthread
自动处理线程的加入(join)操作,避免了程序员忘记加入线程导致的资源泄露问题。此外,std::jthread
还内置了对停止令牌(stop token)的支持,使得线程的停止操作更加简单和安全。
主要特性
-
自动加入:
std::jthread
对象在销毁时会自动检查其关联线程是否仍在执行,如果是,它会等待线程完成(即自动调用join
),这避免了资源泄露和未定义行为。 -
停止支持:
std::jthread
支持协作式的线程停止机制。每个std::jthread
都关联一个std::stop_token
,可以用来检查是否有停止请求。线程函数可以定期检查这个令牌,并在有停止请求时优雅地终止执行。
std::jthread
通过这些机制简化了线程管理,并提高了代码的安全性和可读性。
————————————————————————————————————————
5. 改进的智能指针:
在C++20中,智能指针本身并没有太大的改动,std::unique_ptr
、std::shared_ptr
和 std::weak_ptr
的基本功能和用法与C++11标准引入时保持一致。不过,C++20标准库中确实增加了一些与智能指针相关的辅助功能,使得智能指针的使用更加方便和强大。
std::make_unique
和 std::make_shared
的改进
C++20对std::make_unique
和std::make_shared
进行了扩展,以支持创建具有非公共构造函数的对象。这是通过引入了对std::allocator
的支持来实现的。
std::shared_ptr
的原子操作
C++20增加了对std::shared_ptr
原子操作的支持,这意味着可以安全地在多线程环境中对std::shared_ptr
对象进行读写操作,而不需要额外的同步机制。这些原子操作包括std::atomic_load
、std::atomic_store
、std::atomic_exchange
等。
std::shared_ptr
for std::weak_ptr
在C++20中,你可以直接使用std::weak_ptr
来创建std::shared_ptr
,而不需要先调用std::weak_ptr::lock
。这使得代码更简洁,并且在某些情况下可以提高性能。
std::enable_shared_from_this
C++20中,std::enable_shared_from_this
获得了一个新的成员函数weak_from_this
,它返回一个std::weak_ptr
,指向enable_shared_from_this
对象的当前实例。这使得从当前对象获取std::weak_ptr
变得更加容易。
std::dynamic_pointer_cast
的优化
虽然不是C++20特有的,但std::dynamic_pointer_cast
在动态转换智能指针类型时非常有用,尤其是在多态类层次结构中。C++20中,标准库的实现可能对这些转换操作进行了进一步的优化。
————————————————————————————————————————
6. 新容器:std::array
和 std::basic_string
扩展:
std::array
的改进
在C++20中,std::array
获得了一些新的成员函数和特性:
-
constexpr
支持:std::array
的很多成员函数在C++20中被标记为constexpr
,这意味着它们可以在编译时被执行。这包括begin()
,end()
,size()
,operator[]
等。 -
contains
方法:虽然std::array
没有直接获得contains
方法(通常与关联容器如std::set
和std::map
相关联),但你可以通过使用std::find
来检查std::array
中是否存在某个元素。
std::basic_string
的扩展
-
starts_with
和ends_with
:这两个新方法允许你检查字符串是否以特定的前缀开始或以特定的后缀结束。这使得处理和检查字符串更加方便。std::string s = "Hello, World!"; bool starts = s.starts_with("Hello"); // true bool ends = s.ends_with("World!"); // true
-
remove_prefix
和remove_suffix
:这两个方法允许你从字符串中移除前缀或后缀,简化了字符串处理操作。std::string s = "Hello, World!"; s.remove_prefix(7); // "World!" s.remove_suffix(6); // "Worl"
-
contains
方法:C++20为std::basic_string
添加了contains
方法,可以用来检查字符串中是否包含另一个字符串或字符。std::string s = "Hello, World!"; bool contains = s.contains("World"); // true
————————————————————————————————————————
7. 日期与时间库改进:
C++20标准中引入了一个重要的新库:日期与时间库,正式名称为 <chrono>
库的扩展。
主要特性
-
日期类型:包括
std::chrono::year
,std::chrono::month
,std::chrono::day
等,用于表示年、月、日。 -
时间点:
std::chrono::time_point
被扩展以支持日期,可以表示一个具体的日期点。 -
持续时间类型:C++11中引入的
std::chrono::duration
被保留,并扩展了更多预定义的持续时间类型,如years
,months
,weeks
,days
,hours
,minutes
,seconds
,milliseconds
,microseconds
,nanoseconds
等。 -
时区支持:引入了时区处理的功能,包括对UTC时间和本地时间的转换,以及时区的查询和转换。
-
日历算法:提供了一系列的日历算法,如计算某个月的天数,判断闰年等。
-
格式化和解析:
<chrono>
扩展提供了对日期和时间的格式化和解析功能,使得将日期和时间转换为字符串,或从字符串解析日期和时间变得非常简单。
————————————————————————————————————————
8. 其他标准库容器和算法改进:
包括额外的抽象、增强并行算法、以及新的容器适配器如 `
【大厂面经、学习笔记、实战项目、大纲路线、讲解视频 领取文档】C++校招实习、社招、面试题
https://docs.qq.com/doc/DR2N4d25LRG1leU9Q
C/C++Linux服务器开发/高级架构师学习资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等)领取君羊739729163
合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)