c++11标准
C++11 标准是 C++ 语言的一个重大更新,引入了许多新的语言特性和库功能,旨在提高代码的可读性、性能和编写效率。以下是 C++11 标准中最重要的几个改进和新增特性:
1. 自动类型推导 (auto)
C++11 引入了 auto 关键字,允许编译器根据右值推导变量类型。它极大简化了复杂类型的声明,并让代码更简洁。
示例:
auto x = 42; // x 被推导为 int
auto y = 3.14; // y 被推导为 double
auto z = someFunc(); // z 的类型根据 someFunc() 的返回值推导
2. 范围 for 循环
C++11 引入了基于范围的 for 循环,使得遍历容器变得更简便。
示例:
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto v : vec) {
std::cout << v << std::endl;
}
3. Lambda 表达式
Lambda 表达式是 C++11 的一个非常重要的特性,它提供了内联定义匿名函数的能力,常用于回调、函数对象等场景。
语法:
[捕获列表](参数列表) -> 返回类型 { 函数体 }
示例:
auto add = [](int a, int b) -> int {
return a + b;
};
std::cout << add(1, 2) << std::endl;
4. 右值引用与移动语义 (&& 和 std::move)
C++11 引入了右值引用(T&&),用于实现移动语义,解决拷贝开销问题。通过右值引用和 std::move,可以避免不必要的深拷贝,提升性能。
示例:
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = std::move(vec1); // vec1 的资源被移动到 vec2 中
移动构造函数与移动赋值运算符:
class MyClass {
public:
MyClass(MyClass&& other) { /* 实现移动构造 */ }
MyClass& operator=(MyClass&& other) { /* 实现移动赋值 */ }
};
5. 智能指针(std::unique_ptr 和 std::shared_ptr)
C++11 引入了 std::unique_ptr 和 std::shared_ptr,这是现代 C++ 中管理动态内存的推荐方式,减少了手动管理内存的风险(如内存泄漏)。
std::unique_ptr:唯一拥有某个资源,不能拷贝,但可以移动。 std::shared_ptr:多个指针共享同一资源,使用引用计数来管理资源的生命周期。 示例:
std::unique_ptr<int> p1(new int(10)); // 独占所有权
std::shared_ptr<int> p2 = std::make_shared<int>(20); // 共享所有权
6. nullptr
C++11 用 nullptr 取代了 C++98 的 NULL,使指针与整数 0 更明确地区分。
示例:
int* ptr = nullptr;
7. 静态断言 (static_assert)
静态断言允许在编译时进行条件检查,提高了代码的健壮性和可维护性。
示例:
static_assert(sizeof(int) == 4, "int 大小必须是 4 字节");
8. constexpr
constexpr 关键字允许函数或变量在编译时求值,这对常量表达式的优化有很大帮助。
示例:
constexpr int square(int x) {
return x * x;
}
int arr[square(5)]; // 编译时就能计算出 arr 的大小为 25
9. std::thread 和并发库
C++11 提供了标准线程支持,通过 std::thread 类启动线程,使用互斥量(std::mutex)、锁(std::lock_guard)等进行同步控制。
示例:
void task() {
std::cout << "Task running" << std::endl;
}
std::thread t(task);
t.join(); // 等待线程结束
10. std::tuple
C++11 引入了 std::tuple,它是一种可以存储任意数量不同类型值的容器。
示例:
std::tuple<int, double, std::string> t(1, 2.3, "C++11");
int i;
double d;
std::string s;
std::tie(i, d, s) = t; // 解包 tuple
11. std::initializer_list
C++11 引入了 std::initializer_list,支持通过大括号列表初始化容器或类的成员变量。
示例:
std::vector<int> vec = {1, 2, 3, 4}; // 初始化列表
12. 枚举类 (enum class)
C++11 引入了枚举类,它提高了类型安全性,避免了传统枚举类型的隐式转换问题。
示例:
enum class Color { Red, Green, Blue };
Color c = Color::Red;
13. decltype
decltype 关键字可以用于获取表达式的类型,常用于模板编程和自动推导复杂表达式的返回类型。
示例:
int x = 10;
decltype(x) y = 20; // y 的类型与 x 相同,即 int
14. 移动捕获(Lambda 改进)
C++11 中 Lambda 表达式可以通过 [=] 捕获外部变量,但有时需要显式地移动某些变量。为此,C++11 提供了捕获列表中的 std::move。
示例:
auto ptr = std::make_unique<int>(10);
auto lambda = [p = std::move(ptr)] { std::cout << *p << std::endl; };
c++14标准
C++14 是 C++11 标准的一个增量更新,它的目标是对 C++11 做出一些修订和小改进。C++14 并没有像 C++11 那样引入大量的新特性,但它对 C++11 的语言和库进行了优化、简化,提升了编程体验和性能。以下是 C++14 的主要改进和新增特性:
1. 泛型 Lambda 表达式改进
C++14 允许 Lambda 表达式的参数使用自动类型推导(即 auto),从而支持更广泛的泛型编程。这简化了 Lambda 表达式的使用,尤其是涉及模板的情况下。
C++11 的 Lambda:
auto lambda = [](int x, double y) {
return x + y;
};
C++14 的 Lambda:
auto lambda = [](auto x, auto y) {
return x + y; // 可以传递不同类型的参数
};
std::cout << lambda(1, 2.5) << std::endl; // 结果为 3.5
2. auto 返回类型推导
C++11 引入了 auto 关键字,但在函数返回类型上,必须显式声明返回类型,或者使用尾随返回类型。C++14 则允许编译器自动推导函数的返回类型。
示例:
auto add(int x, int y) {
return x + y; // 编译器自动推导返回类型为 int
}
在 C++11 中,如果使用 auto,需要这样写:
auto add(int x, int y) -> int {
return x + y;
}
3. 常量表达式中的更多功能 (constexpr)
C++14 对 constexpr 关键字做了进一步扩展,允许 constexpr 函数变得更灵活。C++11 中的 constexpr 函数必须是简单的一行计算,C++14 则允许在 constexpr 函数中使用更复杂的语句,如循环和条件语句。
示例:
constexpr int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; ++i)
result *= i;
return result;
}
int arr[factorial(5)]; // 编译时计算 5 的阶乘,arr 的大小为 120
4. std::make_unique
在 C++11 中,智能指针 std::unique_ptr 是引入的重要特性,但当时没有类似于 std::make_shared 的创建 std::shared_ptr 的便捷工厂函数。C++14 引入了 std::make_unique,用于创建 std::unique_ptr 对象,使代码更加安全和简洁。
示例:
auto ptr = std::make_unique<int>(10); // 创建一个智能指针
在 C++11 中,你需要这样写:
std::unique_ptr<int> ptr(new int(10));
std::make_unique 优点包括:
避免手动调用 new,减少内存泄漏风险。 更加高效,因为它可以防止对象初始化和指针赋值的两步操作。
5. 二进制字面量
C++14 引入了二进制字面量,使得以二进制形式表示数值更加直观。通过前缀 0b 或 0B 来定义二进制数字。
示例:
int binary = 0b1010; // 二进制 1010 对应十进制的 10
这个功能对处理位运算、嵌入式编程或需要直接操作硬件的场景尤其有用。
6. 数字分隔符(单引号)
C++14 引入了数字分隔符,可以用单引号(')来分隔数字,使得长数字更易于阅读。
示例:
int million = 1'000'000; // 等价于 1000000
long hex = 0x1'ABC'DEF;
这种写法不会影响数字的数值,纯粹是为了提升可读性,尤其是在金融和科学计算中处理大数时非常方便。
7. 返回类型后置推导改进
在 C++11 中,函数的返回类型推导只能通过“尾随返回类型”来实现。而 C++14 引入了可以直接使用 decltype(auto),让返回类型的推导更加灵活。
示例:
int x = 10;
decltype(auto) func() {
return (x); // 返回类型是 int&
}
这种写法可以确保返回的表达式类型与实际表达式类型保持一致(包括引用)。
8. std::integer_sequence 和 std::index_sequence
这些新工具极大简化了与参数包(variadic template)相关的元编程,它们允许开发者为编译时生成的一系列整数类型提供一个索引序列。这对于一些需要展开参数包的场景非常有用。
示例:
template<std::size_t... Indices>
void print_indices(std::index_sequence<Indices...>) {
((std::cout << Indices << " "), ...);
}
int main() {
print_indices(std::make_index_sequence<5>{}); // 输出:0 1 2 3 4
}
9. [[deprecated]]
属性
C++14 引入了 [[deprecated]]
属性,用于标记不推荐使用的函数或类,编译器会在使用这些标记的元素时给出警告。
示例:
[[deprecated("使用新函数 newFunc 代替")]]
void oldFunc() {}
void newFunc() {}
这样当程序员调用 oldFunc 时,编译器会提示使用新函数 newFunc。
10. 扩展的捕获(Lambda 表达式)
C++14 Lambda 表达式的捕获列表支持直接移动捕获。C++11 只允许按值或按引用捕获外部变量,而 C++14 支持通过移动语义捕获。
示例:
auto ptr = std::make_unique<int>(42);
auto lambda = [p = std::move(ptr)]() {
std::cout << *p << std::endl; // 打印 42
};
总结
C++14 标准在 C++11 的基础上,做了许多实用性、可读性和性能方面的改进。其主要的目标是通过对语言和库的小幅更新,改善开发者体验,使得编写高效、简洁的 C++ 代码更加容易。C++14 的特性虽然没有 C++11 那样革命性,但它解决了一些开发者在使用 C++11 时遇到的实际问题,并为进一步的标准(如 C++17 和 C++20)奠定了基础。
c++17标准
C++17 是 C++ 标准中的一个重要更新,带来了许多新的语言特性和库功能。相比 C++11 和 C++14,C++17 既包含了一些编译时和运行时的优化,也引入了更多便利开发者的功能。以下是 C++17 的主要新特性和改进:
1. 结构化绑定(Structured Bindings)
C++17 引入了结构化绑定,可以将结构、元组或数组的元素直接绑定到局部变量中。这使得从复杂类型中解包数据变得更加简洁。
示例:
std::tuple<int, double, std::string> t(1, 2.3, "C++17");
auto [x, y, z] = t; // 直接将 tuple 的元素解包到 x, y, z
struct Point { int x; int y; };
Point p { 10, 20 };
auto [a, b] = p; // 将 Point 的成员解包到 a 和 b
2. if constexpr
if constexpr 是 C++17 中对条件编译的改进,它允许在编译时根据条件选择执行代码。与普通的 if 不同,如果 if constexpr 的条件为 false,则其对应的代码块不会被编译,从而避免了无效代码的编译错误。
示例:
template<typename T>
void print(T t) {
if constexpr (std::is_integral<T>::value) {
std::cout << t << " is an integer" << std::endl;
} else {
std::cout << t << " is not an integer" << std::endl;
}
}
这里 if constexpr 允许在编译时决定哪个分支会被执行,另一个分支则不会被编译,确保编译时的安全性和性能。
3. 折叠表达式(Fold Expressions)
C++17 中的折叠表达式用于简化对参数包(variadic template)的处理。它允许使用参数包时的递归展开更直观和简洁。
示例:
template<typename... Args>
auto sum(Args... args) {
return (... + args); // 折叠表达式,计算所有参数的和
}
std::cout << sum(1, 2, 3, 4); // 输出:10
在这里,(... + args) 是一个右折叠表达式,它自动展开成 1 + 2 + 3 + 4。
4. std::optional
std::optional 是一个新的容器类型,用于表示一个值可能存在,也可能不存在。这种类型有助于避免空指针或无效状态,提供了更安全的 API。
示例:
std::optional<int> find(int value) {
if (value == 42) {
return 42; // 返回有效值
}
return std::nullopt; // 返回无效值
}
auto result = find(42);
if (result) {
std::cout << "Found: " << *result << std::endl;
} else {
std::cout << "Not found" << std::endl;
}
std::optional 可以避免使用 nullptr 或 0 表示“无效值”的习惯,增强代码的可读性和安全性。
5. std::variant
std::variant 是一种类型安全的联合体,它允许存储多种不同类型的值,但每次只能存储其中一种类型。它提供了比传统联合体(union)更安全的接口,并且支持类型检查。
示例:
std::variant<int, double, std::string> v;
v = 42;
v = "Hello";
if (std::holds_alternative<std::string>(v)) {
std::cout << "Variant holds a string: " << std::get<std::string>(v) << std::endl;
}
std::variant 非常适合需要在运行时存储多种类型但又不想牺牲类型安全的场景。
6. std::any
std::any 是 C++17 引入的一个类型安全的容器,它可以存储任意类型的值,但与 std::variant 不同,std::any 并不限定存储的类型,允许存储任何类型,并在运行时通过 std::any_cast 来提取该值。
示例:
std::any a = 1;
a = std::string("C++17");
try {
std::cout << std::any_cast<std::string>(a) << std::endl; // 输出:C++17
} catch (const std::bad_any_cast& e) {
std::cout << "Bad cast" << std::endl;
}
std::any 非常灵活,但也因此牺牲了一些类型安全,因此通常只在必须的时候使用。
7. 平行算法(Parallel Algorithms)
C++17 的标准库中增加了对并行算法的支持,许多 STL 算法现在可以通过新引入的执行策略并行执行。执行策略包括顺序(std::execution::seq)、并行(std::execution::par)、以及并行且矢量化(std::execution::par_unseq)等。
示例:
#include <execution>
#include <vector>
#include <algorithm>
std::vector<int> vec = {1, 2, 3, 4, 5};
std::for_each(std::execution::par, vec.begin(), vec.end(), [](int& n) {
n *= 2; // 并行执行
});
通过引入并行算法,C++17 提供了更高效的方式来处理大数据集和计算密集型任务。
- constexpr 的改进 C++17 对 constexpr 进行了进一步的增强,允许 constexpr 函数执行更多复杂操作,比如定义局部变量、条件语句、循环等,使得在编译时执行的计算更加灵活。
示例:
constexpr int factorial(int n) {
int result = 1;
for (int i = 2; i <= n; ++i) {
result *= i;
}
return result;
}
static_assert(factorial(5) == 120, "Factorial calculation is wrong");
这使得编译时的常量计算功能更强大,可以更有效地优化代码。
9. 内联变量(Inline Variables)
C++17 引入了内联变量(inline variables)的概念,允许在头文件中定义全局变量或静态成员变量,而不会引发链接错误。这解决了 C++98 和 C++11 中需要在 .cpp 文件中定义全局变量的痛点。
示例:
struct MyClass {
static inline int value = 10; // 内联静态成员变量
};
内联变量可以定义在头文件中,并且可以在多个翻译单元中共享,而不必担心链接冲突。
10. 标准库容器的改进
C++17 对标准库容器进行了若干改进,包括:
std::vector 和 std::string 支持 data() 方法,用于返回底层数组的指针,这在处理与 C API 交互时尤其有用。 std::map 和 std::set 的 insert 和 emplace 函数返回插入位置的迭代器,以简化代码。 示例:
std::vector<int> v = {1, 2, 3};
int* p = v.data(); // 直接获取底层数组指针
11. 编译时静态断言消息改进
C++17 改进了静态断言(static_assert)的语法,使其可以省略错误消息。在 C++11 中,static_assert 必须提供一个错误消息,而 C++17 中可以直接省略消息。
示例:
static_assert(sizeof(int) == 4); // 不需要提供错误消息
c++20标准
C++20 是 C++ 标准中的一次重要更新,被认为是自 C++11 以来最具影响力的标准之一。它带来了大量新特性和改进,涵盖了语言、库、并发、编译时编程等多个方面。以下是 C++20 的一些关键特性及其详细解释。
1. 概念 (Concepts)
概念(Concepts)是 C++20 中的一个核心特性,用于定义模板的约束条件。它们通过提供明确的接口要求,简化了模板编程,并在编译期间捕获模板参数的错误,从而提高了代码的可读性和安全性。
示例:
template<typename T>
concept Integral = std::is_integral_v<T>;
template<Integral T>
T add(T a, T b) {
return a + b;
}
int main() {
std::cout << add(3, 4) << std::endl; // OK
// std::cout << add(3.0, 4.0) << std::endl; // 编译错误,不满足 Integral 概念
}
概念通过约束模板参数类型,避免了模板引发的晦涩错误,提供了更清晰的编译时信息。
2. 协程 (Coroutines)
协程(Coroutines)是 C++20 中引入的一个高级语言特性,允许编写异步代码和生成器。协程通过 co_await、co_yield 和 co_return 关键字提供了暂停、恢复和异步操作的机制,使得异步编程和生成器的实现更加高效和简洁。
示例:
#include <coroutine>
#include <iostream>
struct SimpleCoroutine {
struct promise_type {
SimpleCoroutine get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
};
SimpleCoroutine myCoroutine() {
std::cout << "Hello from coroutine!" << std::endl;
co_return;
}
int main() {
myCoroutine();
}
协程可以在处理 I/O 密集型任务、游戏循环、并发编程等场景中大显身手,减少上下文切换的开销。
3. 范围 (Ranges)
C++20 引入了新的 Ranges 库,这是对现有 STL 算法和迭代器的一次重大改进。Ranges 提供了一种更简洁、更安全的方式来操作集合。它支持基于管道操作符的懒计算链式调用,让代码更具表达性。
示例:
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};
auto result = numbers | std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; });
for (int n : result) {
std::cout << n << " "; // 输出:4 16 36
}
}
Ranges 库简化了数据流处理,减少了对迭代器的显式操作,极大提升了可读性和可维护性。
4. 模块 (Modules)
模块(Modules)是对 C++ 代码的组织方式的一次重大变革,旨在替代传统的头文件机制。模块可以减少编译时间,避免头文件包含的冗余问题,并且改善代码的封装和安全性。
示例:
// mymodule.ixx
export module mymodule;
export int add(int a, int b) {
return a + b;
}
// main.cpp
import mymodule;
int main() {
std::cout << add(1, 2) << std::endl;
}
模块通过显式的导入(import)和导出(export),避免了头文件和实现文件之间的重复,并提升了大规模项目的编译速度。
5. 三向比较 (Three-way Comparison, <=>)
C++20 引入了三向比较运算符,也叫“飞船运算符”(<=>),它可以自动生成比较函数,用于简化对象比较的实现。<=> 运算符返回一个结果,用于表示小于、等于或大于的关系。
示例:
#include <compare>
struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // 自动生成比较操作
};
int main() {
Point p1{1, 2}, p2{1, 3};
std::cout << std::boolalpha << (p1 < p2) << std::endl; // 输出:true
}
三向比较运算符简化了多个比较运算符的编写,特别适用于需要排序的对象。
6. consteval 和 constinit
C++20 引入了两个新的关键字:consteval 和 constinit,用于改进编译时常量表达式的处理。
- consteval:声明一个函数为 consteval,意味着这个函数只能在编译时被调用,任何试图在运行时调用它的行为都会导致编译错误。consteval 强调了编译时求值的必要性,确保调用该函数的所有表达式在编译时都能求值,增强了类型安全性。
- constinit:当使用 constinit 声明一个变量时,编译器会确保该变量在定义时初始化,并且只允许常量表达式作为其初始值。constinit 防止了可能的未初始化常量的运行时行为,确保在使用该变量之前,它已经被初始化并且是常量的。
示例:
consteval int square(int n) {
return n * n;
}
constinit int value = square(4); // 确保在编译时初始化
这些特性增强了对常量表达式的控制,避免了潜在的运行时错误。
7. 范围 for 循环的增强
C++20 对范围 for 循环做了进一步的增强,现在可以直接解包 pair 或结构体成员。
示例:
#include <map>
#include <iostream>
int main() {
std::map<int, std::string> m = {{1, "one"}, {2, "two"}};
for (auto [key, value] : m) {
std::cout << key << ": " << value << std::endl;
}
}
这种语法糖让代码更加简洁,适用于需要处理键值对或结构体的场景。
8. std::span
std::span 是一种轻量级的非拥有类型视图,表示一段连续的内存块。它非常适合用于处理数组或容器的子集,而不需要复制数据。
示例:
#include <span>
#include <vector>
#include <iostream>
void print_span(std::span<int> s) {
for (int n : s) {
std::cout << n << " ";
}
std::cout << std::endl;
}
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
print_span(v); // 输出:1 2 3 4 5
print_span(v.data()); // 输出:1
}
std::span 在处理数组、容器的子集以及与 C 风格数组交互时非常有用,它不涉及数据所有权,提供了安全的边界检查。
9. 范围 for 循环中的初始化
C++20 增加了在范围 for 循环中使用初始化的能力。这意味着可以在 for 循环的范围表达式之前引入一个初始化语句,从而简化代码结构。
示例:
#include <vector>
int main() {
for (std::vector<int> v = {1, 2, 3, 4}; auto n : v) {
std::cout << n << " ";
}
}
这种增强使得代码的可读性和结构性得到了提升,尤其是在需要预处理数据的情况下。
10. std::format
C++20 引入了 std::format,一个功能强大的字符串格式化库,类似于 Python 的 format 函数,取代了之前的 sprintf 和 std::ostringstream,提供了更现代的格式化机制。
示例:
#include <format>
#include <iostream>
int main() {
std::string name = "C++20";
std::cout << std::format("Hello, {}!", name) << std::endl; // 输出:Hello, C++20!
}
std::format 提供了更简洁、安全的格式化方式,避免了传统 printf 风格带来的潜在安全问题。
11. 异步任务的改进 (std::jthread)
C++20 引入了 std::jthread,一种改进版的 std::thread,它会在作用域结束时自动中止线程,避免了手动 join 的麻烦。
示例:
#include <thread>
#include <iostream>
void task() {
std::cout << "Running in jthread" << std::endl;
}
int main() {
std::jthread t(task); // jthread 会自动 join
}
std::jthread 通过简化线程管理,减少了编写并发代码时的错误风险。
总结 C++20 是对 C++ 语言标准的一个重大更新,涵盖了编译时编程、协程、模块、概念等众多领域。它不仅提升了代码的性能和表达能力,还显著提高了开发者的生产力。C++20 标准旨在让 C++ 编程更加现代、简洁和高效,适应未来的编程需求。
c++23
C++23 是 C++ 语言的一个更新版本,作为 C++20 的继任者,它为开发者带来了一系列新特性、改进和 bug 修复。它延续了现代 C++ 的演进趋势,主要聚焦于提高语言的简洁性、功能性和性能。以下是 C++23 的一些重要更新和新特性。
1. 语言特性改进
1.1 模块化 (Modules) 的完善 C++20 引入了模块化系统,但还有许多未解决的问题。C++23 对其进行了多项改进,例如更好的跨模块优化、更简洁的编译和链接过程。模块有助于减少头文件的包含依赖性,降低编译时间。
constexpr 的增强
C++23 中进一步扩展了 constexpr 的适用性,使得更多函数、容器、算法可以在编译时执行。例如:
动态分配的支持:允许 new 和 delete 在 constexpr 中使用。 std::string 和其他标准库容器可以在 constexpr 环境下使用。 这使得更多的编译时计算变得可能,进一步提高程序的性能。
Pattern Matching(模式匹配)初步实现
虽然完整的模式匹配尚未完全实现,但 C++23 引入了基础的结构化绑定和模式匹配功能。这让代码中对某些数据结构的拆解和访问更加简洁。例如,std::variant 和类似类型可以通过模式匹配进行简洁的类型匹配和处理。
if consteval
if consteval 是 C++23 引入的新语法,它允许在编译期选择执行路径。与 constexpr 类似,但针对编译时和运行时的行为区分更为明确。例如:
void foo() {
if consteval {
// 编译时执行的代码
} else {
// 运行时执行的代码
}
}
这种特性让开发者能更加细粒度地控制编译时和运行时的行为。
2. Ranges 库的扩展
C++20 引入的 Ranges 库在 C++23 中得到了进一步扩展和完善。C++23 增加了更多与 Ranges 相关的算法,改进了 view 的操作,增加了范围适配器的灵活性。例如:
新增 std::ranges::to,可以方便地将范围转换为容器。 引入了 join_with 视图,可以将多个 range 连接成一个。 lazy_split 允许延迟分割范围。 这些改进让 Ranges 库在处理复杂数据流时更加自然和简洁。
3. std::expected 类型
C++23 引入了 std::expected,用于处理可能失败的函数返回值。这是对 std::optional 和异常机制的补充,提供了另一种错误处理的模式。std::expected 可以显式表示函数成功或失败的结果,而无需抛出异常或使用 std::optional 表示缺值。
使用示例:
std::expected<int, std::string> get_value(bool succeed) {
if (succeed) {
return 42;
} else {
return std::unexpected("Error occurred");
}
}
4. std::flat_map 和 std::flat_set
C++23 引入了 std::flat_map 和 std::flat_set 作为 std::map 和 std::set 的替代品。它们采用了平坦数组来存储数据,提供了比基于树结构的容器更好的缓存局部性,从而提高了性能,特别是在频繁的查找操作中。
5. std::print
C++23 引入了 std::print 和 std::println,提供了一个简单的标准化打印函数,类似于其他语言中的 printf 或 println,避免了使用 std::cout 的复杂性。例如:
std::print("Hello, World!\n");
这简化了基础的输出操作,避免了 iostream 的冗长语法。
6. 并发和多线程支持
std::jthread 的改进
std::jthread 是 C++20 中引入的类,它简化了线程的管理,自动加入线程而无需手动调用 join。C++23 对此类进行了进一步的增强,包括对线程中断(stop token)的更好支持,使得多线程编程更加安全和高效。
std::atomic_ref
C++23 引入了 std::atomic_ref,它允许对现有对象的原子操作,而无需将对象本身声明为 std::atomic。这为处理非原子对象的线程安全操作提供了更灵活的手段。
7. constexpr 动态内存管理
C++23 允许在 constexpr 环境中动态分配内存,这极大增强了 constexpr 的能力,允许在编译时进行复杂的内存操作和管理。
8. 基于 contract 的编程(延期)
C++20 原本计划引入的 contracts 机制,在 C++23 中仍然被推迟。Contracts 允许在函数定义中指定前置条件、后置条件和不变式,提供更强的约束条件和调试支持。然而由于设计复杂性,它仍未被引入。
9. 占位符类型推导(deducing this)
C++23 引入了 deducing this 特性,允许通过占位符进行成员函数的类型推导。这简化了某些情况下成员函数模板的使用,尤其是在返回类型依赖于 this 的情况下。
C++23 与 C++20 的对比, C++23 更加强调性能和编译期能力(如 constexpr 的增强)。模式匹配和 Ranges 库的改进进一步提升了代码的表达能力。C++23 更注重标准库的扩展,如 std::expected 和 std::flat_map,提供了更好的开发者工具。
C++23 对并发的改进,使多线程编程变得更加易用和安全。总结来说,C++23 是 C++20 的自然进化版本,通过进一步增强编译期计算、标准库的功能、并发编程的支持,以及更简洁的语法,提升了开发者的编程体验和程序的性能。