61 / 80 · C++23 · 约 10 分钟
C++23 expected 与按特性确认可用性
std::expected<T,E> 把成功值与失败原因作为同一个返回类型交付,适合预期内、需要调用者处理的失败。启用 C++23 模式不代表整个标准库已实现,应同时检查编译器、标准库版本和对应特性宏。
成功值与错误都成为接口的一部分
C++23 的 expected<T, E> 在成功时管理一个 T,在失败时管理一个 E。它不是把异常藏起来的语法,也不会自动处理失败;调用者仍应检查 has_value 或条件结果。与 optional 相比,它保留失败原因;与任意 variant 相比,它明确表达成功与错误两条路径。
示例解析一位数字,返回 int 或枚举错误。长度错误与非数字错误分开表达,零仍是合法成功值。使用 unexpected 构造失败结果,能在返回处直接看出失败分支,而不必让调用者解释某个负整数究竟是数据还是错误码。
访问与传播都需要状态纪律
只有成功时才能直接解引用 expected;只有失败时才能读取 error。value 提供带异常的检查访问,但一旦设计采用显式错误分支,通常应在分支内读取正确对象。expected 不能保证构造或复制 T、E 的过程不抛异常,因此不等于整个函数天然 noexcept。
and_then、transform 等单子操作能表达成功路径的继续计算与映射,但它们有独立的库实现进度。基础 expected 的特性宏门槛为 202202L,单子操作门槛为 202211L。这里仅使用基础接口,不把工具链支持基础类型误当成支持所有后续成员。
版本号是入口,特性探测才是证据
站点建议以 C++20 为共同基线,本节示例需 C++23 模式;在 g++ 13.3 配套 libstdc++ 环境可使用这里的基础 expected。不同标准库搭配可能改变可用性,不能只看编译器名字或 -std=c++23 就宣称所有 C++23 特性齐备。
实际接入时包含对应头文件或 version,检查 __cpp_lib_expected 是否达到所需值,再对真实使用路径做编译运行验证。语言特性与库特性应分别检查;ranges::to、print、generator 等也各有实现进度。迁移应以具体能力清单推进,而不是一次性把整个项目贴成“完全支持 C++23”。
容易答错的地方
- 成功时调用 error 或失败时直接解引用,都违反状态前置条件;expected 不是自动保护所有访问的动态对象。
__cplusplus说明所选语言模式,不能证明标准库提供全部功能;基础 expected 与单子成员也应按不同宏值区分。
运行一个例子
最低标准 C++23 · 完整程序 · 下载 .cpp
#include <cassert>
#include <expected>
#include <iostream>
#include <string_view>
enum class ParseError { wrong_length, not_digit };
std::expected<int, ParseError> parse_digit(std::string_view text) {
if (text.size() != 1) return std::unexpected(ParseError::wrong_length);
const char c = text[0];
if (c < '0' || c > '9') return std::unexpected(ParseError::not_digit);
return c - '0';
}
int main() {
auto good = parse_digit("7");
auto bad = parse_digit("x");
auto empty = parse_digit("");
assert(good && *good == 7);
assert(!bad && bad.error() == ParseError::not_digit);
assert(!empty && empty.error() == ParseError::wrong_length);
std::cout << good.value() << " not_digit\n";
}
在本地编译
g++ -std=c++23 -Wall -Wextra -Wpedantic -pthread modern-cpp23.cpp -o example && ./example预期结果
7 not_digit
CHECK YOUR UNDERSTANDING
合上答案,试着解释。
若只想获得失败时的默认值零,可以怎样读取 good 与 bad?这样会丢失什么信息?
查看参考答案
分别调用 good.value_or(0) 与 bad.value_or(0),结果为 7 与 0。但失败结果与成功解析字符 '0' 都变成零,错误原因也不会出现在最终数值里。只有业务确实把这些状态视为等价时才应采用默认值,否则保留 expected 并显式分支。
继续查证
标准草案链接会随工作草案更新;本文版本标记对应示例最低要求,不表示草案中的所有新规则都适用于旧标准。