函数可能算不出一个整数:解析失败、查找没有命中。用 -1 当哨兵会和合法负数搅在一起。std::optional<int>(C++17)要么有值,要么为空。std::variant<int, std::string> 则在固定几种类型里只保存其中一种,比 C 的 union 知道当前到底是哪一种,析构也会走对。本节先跑 optional,再跑 variant 与 visit,并对照异常。
std::optional
parse_int("42") 有值,if (auto v = ...) 为真,*v 取出整数。parse_int("x") 为空,value_or(-1) 给出默认值而不抛异常。
#include <iostream>
#include <optional>
#include <string>
std::optional<int> parse_int(const std::string& s) {
try {
return std::stoi(s);
} catch (...) {
return std::nullopt;
}
}
int main() {
if (auto v = parse_int("42")) {
std::cout << *v << "\n";
}
auto bad = parse_int("x");
std::cout << bad.value_or(-1) << "\n";
return 0;
}输出:
42
-1has_value():是否持有值。value():无值时抛std::bad_optional_access。value_or(d):无值时返回d,不抛异常。
std::variant
任意时刻只激活一种类型。用 std::get<T> 取出;类型不对会抛 std::bad_variant_access。holds_alternative<T> 先判断再取。
#include <iostream>
#include <string>
#include <variant>
int main() {
std::variant<int, double, std::string> v = std::string("ok");
std::cout << std::get<std::string>(v) << "\n";
v = 3.14;
if (std::holds_alternative<double>(v)) {
std::cout << std::get<double>(v) << "\n";
}
return 0;
}输出:
ok
3.14不抛异常的取法是 std::get_if<int>(&v),失败返回空指针。需要空状态时,可把 std::monostate 放在类型列表第一位。
std::visit
不想手写一串 holds_alternative 时,把泛型 lambda 交给 visit,编译器按当前类型生成对应调用:
#include <iostream>
#include <string>
#include <variant>
int main() {
std::variant<int, std::string> v = 42;
std::visit([](const auto& x) { std::cout << x << "\n"; }, v);
v = std::string("text");
std::visit([](const auto& x) { std::cout << x << "\n"; }, v);
return 0;
}输出:
42
text与异常、错误码
| 方式 | 适用 |
|---|---|
optional | 失败很常见,且不必区分多种原因 |
错误码 / expected(C++23) | 库边界、调用方必须检查返回值 |
| 异常 | 跨很多层、失败很少见 |
本节说明了 optional、variant 与 visit 的用途。下一节介绍chrono:时间点、时长与单位安全的运算。