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
英文地址,請注明出處:http://en.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…