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 提供了更高效的方式来处理大数据集和计算密集型任务。

  1. 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 的自然进化版本,通过进一步增强编译期计算、标准库的功能、并发编程的支持,以及更简洁的语法,提升了开发者的编程体验和程序的性能。