C++23标准新增的std::expected是一个模板类,用于表示可能成功返回预期值,也可能返回错误的对象,配合monadic操作符可以实现流畅的链式错误处理,避免传统错误处理方式的冗余逻辑。
std::expected基本用法
std::expected<T, E>包含两个可能的状态:一个是存储成功值T,另一个是存储错误值E。我们可以通过构造函数或者std::expected<T,E>::value、std::expected<T,E>::error来访问对应的值,也可以通过has_value方法判断当前状态是成功还是失败。
下面是一个简单的使用示例,模拟读取配置的操作,成功返回配置字符串,失败返回错误信息:
#include <expected>
#include <string>
#include <iostream>
// 模拟读取配置,成功返回配置内容,失败返回错误字符串
std::expected<std::string, std::string> read_config() {
// 模拟读取失败的场景
bool read_success = false;
if (read_success) {
return "config_content";
} else {
return std::unexpected<std::string>("read config failed");
}
}
int main() {
auto result = read_config();
if (result.has_value()) {
std::cout << "config: " << result.value() << std::endl;
} else {
std::cout << "error: " << result.error() << std::endl;
}
return 0;
}
C++23 monadic操作符介绍
C++23为std::expected新增了三个monadic操作符,分别是and_then、transform、or_else,它们都支持链式调用,让错误处理的逻辑更连贯。
and_then操作符
当std::expected处于成功状态时,and_then会执行传入的回调函数,回调函数需要返回一个新的std::expected对象;如果当前处于失败状态,会直接跳过回调,返回当前的错误状态。这个操作符适合处理多个依赖的前置操作,前一个操作成功才执行后续操作。
#include <expected>
#include <string>
#include <iostream>
// 解析配置,成功返回解析后的整数,失败返回错误
std::expected<int, std::string> parse_config(const std::string& config) {
if (config == "config_content") {
return 100;
}
return std::unexpected<std::string>("parse config failed");
}
int main() {
auto result = read_config().and_then(parse_config);
if (result.has_value()) {
std::cout << "parsed value: " << result.value() << std::endl;
} else {
std::cout << "error: " << result.error() << std::endl;
}
return 0;
}
transform操作符
transform同样在成功状态下执行回调,但回调函数的返回值会被自动包装成新的std::expected对象,不需要手动构造。如果当前是失败状态,同样会直接跳过回调返回错误。这个操作符适合对成功值做简单的转换操作。
#include <expected>
#include <string>
#include <iostream>
// 给解析后的配置值加偏移量
int add_offset(int val) {
return val + 50;
}
int main() {
auto result = read_config()
.and_then(parse_config)
.transform(add_offset);
if (result.has_value()) {
std::cout << "final value: " << result.value() << std::endl;
} else {
std::cout << "error: " << result.error() << std::endl;
}
return 0;
}
or_else操作符
or_else在std::expected处于失败状态时执行回调,回调函数可以返回一个新的std::expected对象,也可以做错误日志打印等操作。如果当前是成功状态,会直接跳过回调返回当前值。这个操作符适合做错误兜底处理。
#include <expected>
#include <string>
#include <iostream>
// 错误处理回调,返回默认配置
std::expected<std::string, std::string> handle_error(const std::string& err) {
std::cout << "catch error: " << err << ", use default config" << std::endl;
return "default_config";
}
int main() {
auto result = read_config()
.or_else(handle_error)
.and_then(parse_config);
if (result.has_value()) {
std::cout << "result value: " << result.value() << std::endl;
} else {
std::cout << "still error: " << result.error() << std::endl;
}
return 0;
}
链式错误处理的完整示例
下面把三个操作符结合起来,实现完整的链式错误处理流程:读取配置、解析配置、转换值、错误兜底,整个流程不需要写大量的if判断,逻辑清晰易读。
#include <expected>
#include <string>
#include <iostream>
std::expected<std::string, std::string> read_config() {
bool read_success = false;
if (read_success) {
return "config_content";
} else {
return std::unexpected<std::string>("read config failed");
}
}
std::expected<int, std::string> parse_config(const std::string& config) {
if (config == "config_content" || config == "default_config") {
return 100;
}
return std::unexpected<std::string>("parse config failed");
}
int add_offset(int val) {
return val + 50;
}
std::expected<std::string, std::string> handle_error(const std::string& err) {
std::cout << "catch error: " << err << ", use default config" << std::endl;
return "default_config";
}
int main() {
auto final_result = read_config()
.or_else(handle_error)
.and_then(parse_config)
.transform(add_offset);
if (final_result.has_value()) {
std::cout << "final result: " << final_result.value() << std::endl;
} else {
std::cout << "final error: " << final_result.error() << std::endl;
}
return 0;
}
使用注意事项
- monadic操作符是C++23新增特性,需要编译器支持C++23标准,比如GCC 13+、Clang 16+版本。
- 回调函数的返回值类型需要和链式调用的预期类型匹配,
and_then的回调必须返回std::expected,transform的回调返回普通值即可。 - 链式调用过程中,一旦某个步骤返回错误,后续的操作符都会跳过,直到遇到
or_else处理错误或者最终获取结果。
std::expectedC++23monadic_操作符链式错误处理修改时间:2026-06-12 18:36:50