当前位置: 首页 > news >正文

C++23中的std::expected:异常处理

C++23中的std::expected:异常处理

众所周知,C++23以前的异常处理是比较麻烦的,尤其是自己要在可能抛出异常的地方,需要自己去捕获它,比如除数为0的异常、使用std::stoi函数将字符串转换成int整型数据、处理文件读写的异常等等,不然很容易造成程序终止。
关于C++23中引入的std::expected,这一全新的词汇表类型,它为函数返回结果的处理提供了一种更加优雅、类型安全的解决方案。

std::expected是C++23标准库中的一个模板类,定义于头文件<expected>中。它提供了一种方式来表示两个值之一:类型T的期望值,或类型E的非期望值。std::expected永远不会是无值的。

// Defined in header <expected>
template< class T, class E >
class expected;
(1)	(since C++23)
template< class T, class E >requires std::is_void_v<T>
class expected<T, E>;

类模板 std::expected 提供了一种表示两种值的方式:类型为 T 的预期值或类型为 E 的意外值。预期值永远不会是无值的。

  1. 主模板。在其自身的存储空间中包含预期值或意外值,该存储空间嵌套在预期对象中。
  2. void 部分特化。表示预期 void 值或包含意外值。如果包含意外值,则嵌套在预期对象中。
    如果程序使用引用类型、函数类型或 std::unexpected 的特化来实例化预期值,则程序格式错误。此外,T 不能是 std::in_place_t 或 std::unexpect_t。

模板参数
T - 预期值的类型。该类型必须是(可能为 cv 限定的)void,或者满足Destructible即可析构性要求(特别是,不允许使用数组和引用类型)。
E - 意外值的类型。该类型必须满足Destructible要求,并且必须是 std::unexpected 的有效模板参数(特别是,不允许使用数组、非对象类型和 cv 限定的类型)。

示例程序

#include <cmath>
#include <expected>
#include <iomanip>
#include <iostream>
#include <string_view>enum class parse_error
{invalid_input,overflow
};auto parse_number(std::string_view& str) -> std::expected<double, parse_error>
{const char* begin = str.data();char* end;double retval = std::strtod(begin, &end);if (begin == end)return std::unexpected(parse_error::invalid_input);else if (std::isinf(retval))return std::unexpected(parse_error::overflow);str.remove_prefix(end - begin);return retval;
}int main()
{auto process = [](std::string_view str){std::cout << "str: " << std::quoted(str) << ", ";if (const auto num = parse_number(str); num.has_value())std::cout << "value: " << *num << '\n';// If num did not have a value, dereferencing num// would cause an undefined behavior, and// num.value() would throw std::bad_expected_access.// num.value_or(123) uses specified default value 123.else if (num.error() == parse_error::invalid_input)std::cout << "error: invalid input\n";else if (num.error() == parse_error::overflow)std::cout << "error: overflow\n";elsestd::cout << "unexpected!\n"; // or invoke std::unreachable();};for (auto src : {"42", "42abc", "meow", "inf"})process(src);
}

执行结果如下:

str: "42", value: 42
str: "42abc", value: 42
str: "meow", error: invalid input
str: "inf", error: overflow

油管中TheCherno的一个视频C++ FINALLY Improved Error Handling with std::expected!对于std::exepected讲解得不错,感兴趣的可以去看看。

  • 0:00 - Quick look at std::expected
  • 3:30 - Life before std::expected
  • 8:07 - Using std::expected for error handling
  • 12:25 - Some more useful features of std::expected
  • 17:06 - More advanced use case (file reading)

关于std::expected的代码示例1

#include <iostream>
#include <print>
#include <expected>// https://en.cppreference.com/w/cpp/utility/expected// https://en.cppreference.com/w/cpp/utility/expected/expected// Example of using std::expected in C++23
// This example demonstrates how to use std::expected to handle errors gracefully.
std::expected<int, std::string> divide(int numerator, int denominator) {if (denominator == 0) {return std::unexpected("Division by zero error");}return numerator / denominator;
}int main()
{int a = 10;int b = 0;// Attempt to divide and handle the resultauto result = divide(a, b);if (result) {std::println("Result: {}", *result);} else {std::println("Error: {}", result.error());}// Example with valid divisionb = 2;result = divide(a, b);if (result) {std::println("Result: {}", *result);} else {std::println("Error: {}", result.error());}return 0;
}

运行结果如下:

Error: Division by zero error
Result: 5

关于std::expected的代码示例2-支持链式调用

#include <iostream>
#include <print>
#include <expected>// https://en.cppreference.com/w/cpp/utility/expected// 这是一个示例程序,演示如何使用 std::expected 进行错误处理。
std::expected<int, std::string> divide(int numerator, int denominator) {if (denominator == 0) {return std::unexpected("Division by zero error");return 0; // Return a default value}return numerator / denominator;
}void test_001()
{auto result = divide(10, 2);if (result) {std::println("Result: {}", *result);} else {std::println("Error: {}", result.error());}
}int test_002()
{auto result = divide(12, 3);result = result.and_then([](int value) { return divide(value, 0); }).or_else([](const std::string& error) {std::println("Error occurred: {}", error);return std::expected<int, std::string>{0};});if (result) {std::println("Final Result: {}", *result);}
}int main()
{// 测试 std::expected 的基本用法test_001();// 测试 std::expected 的链式调用和错误处理test_002();return 0;
}

运行结果如下:

esult: 5
Error occurred: Division by zero error
Final Result: 0

关于std::expected的代码示例3

#include <iostream>
#include <expected>
#include <string>
#include <print>// https://en.cppreference.com/w/cpp/utility/expected// Example of using std::expected in C++23
// This example demonstrates how to use std::expected to handle errors gracefully.
// 定义一个可能返回int或者字符串错误的expected类型
std::expected<int, std::string> parse_number(const std::string& str) {try {// 尝试将字符串转换为整数return std::stoi(str);} catch (const std::invalid_argument&) {// 如果转换失败,返回一个错误信息return std::unexpected("Invalid number format");} catch (const std::out_of_range&) {// 如果数字超出范围,返回一个错误信息return std::unexpected("Number out of range");}
}int main()
{auto result = parse_number("123");if (result.has_value()) {std::println("Parsed number: {}", *result);} else {std::println("Error: {}", result.error());}result = parse_number("abc");if (result.has_value()) {std::println("Parsed number: {}", *result);} else {std::println("Error: {}", result.error());}result = parse_number("12345678901234567890");if (result.has_value()) {std::println("Parsed number: {}", *result);} else {std::println("Error: {}", result.error());}return 0;
}

运行结果如下:

Parsed number: 123
Error: Invalid number format
Error: Number out of range 

总结

C++23引入的std::expected为函数返回结果的处理提供了一种更加优雅、类型安全的解决方案。它解决了传统错误处理方法中的一些痛点,如类型安全问题、代码可读性问题和性能开销问题等。通过使用std::expected,开发者可以编写出更加健壮、可维护的代码。在实际开发中,建议开发者积极采用std::expected来处理函数的返回结果,特别是在对性能和代码质量有较高要求的场景中。当然,注意:如果在c++23标准之前的老项目,可能就不支持std::expected这种新特性了。

参考资料

  • https://en.cppreference.com/w/cpp/utility/expected.html
  • C++23 std::expected:一种新的词汇表类型,用于返回函数的结果
  • C++23中的新功能之expected和optional
http://www.dtcms.com/a/289419.html

相关文章:

  • 以“融合进化 智领未来”之名,金仓Kingbase FlySync:国产数据库技术的突破与创新
  • SpringBoot集成Skywalking链路跟踪
  • CAN通讯理论与实践:调试和优化全讲解
  • 20250720-2-Kubernetes 调度-资源限制对Pod调度的影响(1)_笔记
  • 基于深度学习的目标检测:从基础到实践
  • 尚庭公寓--------登陆流程介绍以及功能代码
  • 常见的离散积分方法
  • 基于bert-lstm对微博评论的情感分析系统设计与实现
  • 《每日AI-人工智能-编程日报》--2025年7月20日
  • Direct3D 11学习(一)
  • Charles 的 Windows proxy 对爬取瑞数6 网站接口数据的作用分析
  • 高性能架构模式——单服务器高性能模式(PPC与TPC)
  • 创新几何解谜游戏,挑战空间思维极限
  • 【51单片机仿真复位电阻电容参数】2022-5-17
  • TD3与SAC强化学习算法深度对比
  • BLIP、InternVL Series(下)
  • SSH开启Socks5服务
  • 强化学习_Paper_ICLR2024_When Should We Prefer DECISION TRANSFORMERS for offline-RL
  • 【分布式 ID】详解百度 uid-generator(基础篇)
  • java12基础(day12)
  • 零基础学习性能测试第一章-为什么会有性能问题
  • 【读技术报告】Manner Agent如何管理上下文
  • 从 AlphaGo 到具身机器人:AI 四力阶梯的突破之旅
  • 爬虫实战案例(两个)
  • Open64 WHIRL
  • `tidyverse` 长表、宽表的处理
  • 使用Qt6 QML/C++ 和CMake构建海康威视摄像头应用(代码开源)
  • 看板流程标准化和灵活性如何平衡
  • 在Ubuntu22系统上离线部署ai-infra-guard教程【亲测成功】
  • 深入分析linux内核源代码