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.pswp.cn/bicheng/89887.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/89887.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

处理Electron Builder 创建新进程错误 spawn ENOMEM

这个错误 spawn ENOMEM 表明系统内存不足&#xff0c;无法为 Electron Builder 创建新进程。以下是一些可能的解决方案&#xff1a;释放系统内存关闭不必要的程序和服务增加物理内存 (RAM) 或交换空间 (swap)使用 free -h 和 top 命令检查内存使用情况临时增加交换空间# 创建一…

discuz安装使用教程,及网站部署在公网访问

Discuz!的安装主要包括环境准备、程序部署和配置管理三个核心步骤‌&#xff0c;有条件 的可以使用宝塔面板或云镜像简化流程&#xff0c;本地部署无公网IP的配合类似nat123映射公网访问&#xff0c;当前最新版本为Discuz! Q&#xff08;2025年发布&#xff09;和Discuz! X3.5&…

深入解析C#数字转换:隐式与显式转换的全面指南

—— 数据精度保卫战中的checked与unchecked秘籍 &#x1f4cc; 核心概念速览 1. 隐式转换 自动发生&#xff0c;无数据丢失风险&#xff08;如 int→long&#xff09;遵循"小类型→大类型"路径&#xff08;见下图⬇️&#xff09; [图1&#xff1a;C#隐式数字转换路…

量子计算可以解决的三个现实问题

今年是量子力学被发现一百周年。这一突破帮助人们认识到&#xff0c;支配我们周围世界最小层面&#xff08;分子、原子和亚原子粒子&#xff09;的物理定律&#xff0c;与支配我们在日常生活中与物体相互作用方式的物理定律有着根本的不同。量子力学让我们能够了解从血液中的新…

Valgrind Memcheck 全解析教程:6个程序说明基础内存错误

Valgrind 是一个强大的动态分析框架&#xff0c;其中的 memcheck 工具用于检测 C/C 程序中类型不定的内存错误&#xff0c;是基础级内存调试工具的重要选择。 本文将通过 6 段有意义的错误代码&#xff0c;全面讲解 memcheck 的检测原理和输出分析&#xff0c;进而帮助学习者托…

Vue3 实现 Excel 文件导入导出功能

在Vue 3中实现Excel文件的导入和导出功能&#xff0c;你可以使用一些流行的JavaScript库&#xff0c;如SheetJS&#xff08;也称为xlsx&#xff09;来处理Excel文件。以下是实现这一功能的基本步骤&#xff1a;1. 安装SheetJS首先&#xff0c;你需要安装xlsx库。在你的Vue项目中…

CS231n-2017 Lecture2图像分类笔记

图像分类问题定义&#xff1a;在已有固定的分类标签集合的前提下&#xff0c;能够对输入的图像进行识别处理&#xff0c;从集合中找到该图像所对应的标签。对于计算机而言&#xff0c;图像并非直观的图像&#xff0c;而是一个的像素集合&#xff0c;对于每个像素&#xff0c;其…

Typecho博客Ajax评论功能实现全攻略

文章目录 Typecho实现Ajax评论功能的完整指南 引言 一、技术选型与准备工作 1.1 技术栈分析 1.2 环境准备 二、前端实现方案 2.1 基础HTML结构 2.2 JavaScript处理逻辑 三、后端处理实现 3.1 创建插件处理Ajax请求 3.2 错误处理增强 四、安全性考虑 4.1 CSRF防护 4.2 输入过滤 …

【计算机考研(408)- 数据结构】树与二叉树

树与二叉树 树的定义及相关概念 树是n&#xff08;n≥0&#xff09;个结点的有限集合&#xff0c;n 0时&#xff0c;称为空树&#xff0c;这是一种特殊情况。在任意一棵非空树中应满足&#xff1a; 1&#xff09;有且仅有一个特定的称为根的结点。 2&#xff09;当n > 1时…

MacOS:如何利用终端来操作用户

MacOS&#xff1a;如何利用终端来操作用户MacOS&#xff1a;如何利用终端来操作用户1. 创建用户并赋予管理员权限步骤&#xff1a;2. 取消用户的管理员权限解释&#xff1a;3. 查看组成员查看 admin 组成员&#xff1a;查看 users 组成员&#xff1a;4. 其他常见的用户管理命令…

基于SpringBoot+MyBatis+MySQL+VUE实现的医疗挂号管理系统(附源码+数据库+毕业论文+答辩PPT+项目部署视频教程+项目所需软件工具)

摘 要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&#xff0c;对医疗挂号信息管理的提升&#x…

学成在线项目

黑马程序员学成在线项目学习过程记录 解决跨域问题

Shell脚本-grep工具

一、前言在 Linux/Unix 系统中&#xff0c;grep 是一个非常强大且常用的文本搜索工具&#xff0c;它可以帮助我们快速从文件或标准输入中查找匹配特定模式的内容。无论是查看日志、调试脚本&#xff0c;还是进行自动化数据提取&#xff0c;grep 都扮演着至关重要的角色。本文将…

深入解析Ext2文件系统架构

要在硬盘上存储文件&#xff0c;必须先将硬盘格式化为特定类型的文件系统。文件系统的主要功能就是组织和管硬盘中的文件。在Linux系统中&#xff0c;最常见的文件系统是Ext2系列&#xff0c;其早期版本为Ext2&#xff0c;后续又发展出Ext3和Ext4。虽然Ext3和Ext4对Ext2进行了功…

商业秘密保护:从法律理论到企业实战

作者&#xff1a;邱戈龙、柯坚豪深圳商业秘密律师广东长昊律师事务所在商业竞争中&#xff0c;商业秘密就像企业的"隐形护城河"。从法律角度看&#xff0c;它的保护路径经历了三次重要升级&#xff1a;从最初的"合同约定"到后来的"财产保护"&…

AI产品经理面试宝典第36天:AI+旅游以及行业痛点相关面试题的指导

一、AI如何解决旅游行业核心痛点? 面试官提问: "请结合具体案例说明AI在旅游行业的应用价值,以及它如何解决传统旅游服务的痛点?" 你的回答: 以腾讯"一部手机游云南"为例,AI技术通过四大核心体系重构旅游体验: 数字身份体系:通过人脸识别与用户…

【conda】Linux系统中部署Conda环境

目录 一、安装 Miniconda 1.1 下载 Miniconda 安装脚本 1.2 运行安装脚本 1.3 初始化 Conda&#xff1a; 安装完成后&#xff0c;初始化 Conda 环境 1.4 验证安装 二、设置虚拟环境默认存放路径&#xff08;可选&#xff09; 三、conda创建虚拟环境 3.1 创建 Conda 环境…

Spring Boot 解决跨域问题

在 Spring Boot 中解决跨域问题&#xff08;CORS&#xff09;主要有三种常用方式&#xff0c;下面详细说明每种实现方法&#xff1a; 方案一&#xff1a;全局配置&#xff08;推荐&#xff09; 在配置类中实现 WebMvcConfigurer 接口&#xff0c;统一配置所有接口的跨域规则&am…

Softhub软件下载站实战开发(十九):软件信息展示

上一篇文章中我们上线了软件分离展示&#xff0c;本篇文章我们聚焦软件信息展示 软件列表信息展示 点击一级分类查询该分类下所以软件分类切换要有动画效果分页支持 核心实现 <transition-grouptag"div"class"software-grid"before-enter"before…

[HDLBits] Cs450/gshare

Branch direction predictor 分支方向预测器 A branch direction predictor generates taken/not-taken predictions of the direction of conditional branch instructions. It sits near the front of the processor pipeline, and is responsible for directing instructio…