C++23概述

C++23

概述

C++23 包含以下新的语言特性:

  • [常量求值 if 语句(consteval if)](#常量求值 if 语句(consteval if))
  • [推导 this 指针(deducing this)](#推导 this 指针(deducing this))
  • 多维下标运算符
  • [提升基于范围的 for 循环安全性](#提升基于范围的 for 循环安全性)

C++23 包含以下新的库特性:

  • [堆栈跟踪库(stacktrace library)](#堆栈跟踪库(stacktrace library))
  • [字符串和字符串视图的 contains 方法](#字符串和字符串视图的 contains 方法)
  • std::to_underlying
  • spanstream
  • [输入/输出指针(input/output pointers)](#输入/输出指针(input/output pointers))
  • [std::optional 的单子操作(monadic operations)](#std::optional 的单子操作(monadic operations))
  • std::expected
  • std::unreachable

C++23 语言特性

常量求值 if 语句(consteval if)

编写可在常量求值期间实例化的代码。

c++ 复制代码
consteval int f(int i) { return i; }

constexpr int g(int i) {
  if consteval {
      return f(i);
  } else {
      return 42;
  }
}

推导 this 指针(deducing this

C++23 引入了显式对象成员函数,现在可以通过指定成员函数的第一个参数(以 this 关键字为前缀),推导对象的类型和值类别:

c++ 复制代码
// C++23 新写法(推导 this):
struct T {
  decltype(auto) operator[](this auto& self, std::size_t idx) { 
    return self.mVector[idx]; 
  }
};

// 旧写法:
struct T {
  value_t& operator[](std::size_t idx) {
    return mVector[idx];
  }
  const value_t& operator[](std::size_t idx) const {
    return mVector[idx];
  }
};

多维下标运算符

可为 operator[] 运算符指定零个或多个参数:

c++ 复制代码
template <typename T, std::size_t Z, std::size_t Y, std::size_t X>
struct Array3d {
  std::array<T, X * Y * Z> m{};

  T& operator[](std::size_t z, std::size_t y, std::size_t x) {
      return m[z * Y * X + y * X + x];
  }
};

Array3d<int, 4, 3, 2> v;
v[3, 2, 1] = 42;

提升基于范围的 for 循环安全性

修复了 C++ 中最重要的控制结构之一存在的部分臭名昭著的生命周期问题。

以下是 C++23 之前存在问题、现在已修复的代码片段示例:

  • for (auto e : getTmp().getRef())
  • for (auto e : getVector()[0])
  • for (auto valueElem : getMap()["key"])
  • for (auto e : get<0>(getTuple()))
  • for (auto e : getOptionalCollection().value())
  • for (char c : get<std::string>(getVariant()))

C++23 库特性

堆栈跟踪库(stacktrace library)

堆栈跟踪是调用序列的近似表示,由多个堆栈跟踪条目组成。一个堆栈跟踪条目(由 std::stacktrace_entry 表示)包含的信息包括源文件和行号,以及一个描述字段。

Linux 系统上的示例输出:

c++ 复制代码
#include <print>
#include <stacktrace>

int main() {
    std::println("{}", std::stacktrace::current());
}
复制代码
  0#  main at /app/example.cpp:5 [0x5ee42e3db747]
  1#  <unknown> [0x76e76dc29d8f]
  2#  __libc_start_main [0x76e76dc29e3f]
  3#  _start [0x5ee42e3db644]

字符串和字符串视图的 contains 方法

一种更简洁的方法,用于查询字符串或字符串视图中是否包含某个子串:

c++ 复制代码
std::string{"foobarbaz"}.contains("bar"); // == true
std::string{"foobarbaz"}.contains("bat"); // == false

std::to_underlying

支持将枚举类型转换为其底层类型这一常用工具函数:

c++ 复制代码
enum class MyEnum : int { A = 1, B, C };
std::to_underlying(MyEnum::A); // == 1
std::to_underlying(MyEnum::C); // == 3

spanstream

strstream 的替代方案,使用字符跨度(span)作为外部提供的缓冲区。不拥有缓冲区所有权,也不会对缓冲区进行重新分配。

c++ 复制代码
char input[] = "10 20 30";
std::ispanstream is{std::span<char>{input}};
int i;
is >> i; // i == 10
is >> i; // i == 20
is >> i; // i == 30
c++ 复制代码
char output[30]{}; // 零初始化数组
std::ospanstream os{std::span<char>{output}};
os << 10 << 20 << 30;
std::span<char> sp = os.span();

输入/输出指针(input/output pointers)

std::out_ptrstd::inout_ptr 是一种抽象,通过创建临时的指向指针的指针(pointer-to-pointer)来同时支持 C API 和智能指针,该临时指针会在析构时更新智能指针。简而言之:它是一种可转换为 T** 的类型,当它离开作用域时,会更新(通过调用 reset 或语义等效的行为)创建它时所关联的智能指针。

该抽象还能在抛出异常时安全管理关联内存的生命周期。

c++ 复制代码
// p_handle 仅用于写入(输出)。
int c_api_create_handle(MyHandle** p_handle);
// p_handle 既用于读取(输入),也用于写入(输出)。
int c_api_recreate_handle(MyHandle** p_handle);
void c_api_delete_handle(MyHandle* handle);

struct resource_deleter {
	void operator()(MyHandle* handle) {
		c_api_delete_handle(handle);
	}
};
c++ 复制代码
std::unique_ptr<MyHandle, resource_deleter> resource(nullptr);
int err = c_api_create_handle(std::out_ptr(resource));
// `resource` 现在拥有 `c_api_create_handle` 内部分配的内存。
c++ 复制代码
std::shared_ptr<MyHandle> resource(nullptr);
int err = c_api_recreate_handle(std::inout_ptr(resource), resource_deleter{});
// `resource` 现在共享 `c_api_recreate_handle` 内部分配的内存。

inout/out 指针均支持隐式转换为 void**,也支持显式转换为用户指定的类型。

std::optional 的单子操作(monadic operations)

std::optional 支持多种 and_thentransformor_else 操作。

c++ 复制代码
std::optional<int> parse_int(const std::string&);
std::optional<int> ensure_non_negative(int);
std::optional<double> default_value_or_empty(double);

std::optional<double> stringToSqrtDouble(const std::string& input) {
  return parse_int(input)
    .and_then(ensure_non_negative)
    .transform([](int x) {
      return std::sqrt(x);
    })
    .or_else(default_value_or_empty);
}

std::expected

std::expected 提供了一种方式来表示一个值和一个潜在的错误值,两者都包含在一个类型中。还支持对预期值(expected)和非预期值(unexpected,即错误值)执行多种单子操作。

使用 std::unexpected 存储非预期值(即错误值)。

c++ 复制代码
enum class StringToSqrtDoubleError {
    ParseError, NegativeNumber
};

std::expected<int, StringToSqrtDoubleError> parse_int(const std::string&);

std::expected<double, StringToSqrtDoubleError> stringToSqrtDouble(const std::string& input) {
    auto parsed = parse_int(input);
    if (!parsed) return parsed;

    auto parsedInt = *parsed;
    if (parsedInt < 0) return std::unexpected(StringToSqrtDoubleError::NegativeNumber);

    return std::sqrt(parsedInt);
}

std::unreachable

提供一种显式标记代码路径为"不可达"的方式。如果该代码路径被执行到,可能会表现出未定义行为。

c++ 复制代码
enum class MyEnum { A, B, C };

int convertMyEnumToInt(MyEnum e) {
    switch (e) {
        case MyEnum::A: return 0;
        case MyEnum::B: return 1;
        case MyEnum::C: return 2;
        default: std::unreachable(); 
    }
}
相关推荐
专注API从业者2 小时前
Open Claw 京东商品监控选品实战:一键抓取、实时监控、高效选品
java·服务器·数据库
摇滚侠2 小时前
DBeaver 导入数据库 导入 SQL 文件 MySQL 备份恢复
java·数据库·mysql
keep one's resolveY3 小时前
SpringBoot实现重试机制的四种方案
java·spring boot·后端
学涯乐码堂主3 小时前
有趣的“打擂台算法”
c++·算法·青少年编程·gesp
天空属于哈夫克33 小时前
企业微信API常见的错误和解决方案
java·数据库·企业微信
云栖梦泽3 小时前
Linux内核与驱动:14.SPI子系统
linux·运维·服务器·c++
Gary Studio4 小时前
安卓HAL C++基础-智能指针
开发语言·c++
还是阿落呀4 小时前
基本控制结构2
c++
摇滚侠4 小时前
VMvare 虚拟机 Oracle19c 安装步骤,远程连接 Oracle19c,百度网盘安装包
java·oracle