现代C++24- 处理数据类型变化和错误:optional、variant、expected和Herbception

我们之前已经讨论了异常是推荐的 C++ 错误处理方式。不过,C++ 里有另外一些结构也很适合进行错误处理,今天我们就来讨论一下。

optional

在面向对象(引用语义)的语言里,我们有时候会使用空值 null 表示没有找到需要的对象。也有人推荐使用一个特殊的空对象,来避免空值带来的一些问题 [1]。可不管是空值,还是空对象,对于一个返回普通对象(值语义)的 C++ 函数都是不适用的——空值和空对象只能用在返回引用 / 指针的场合,一般情况下需要堆内存分配,在 C++ 里会引致额外的开销。

C++17 引入的 optional 模板 [2] 可以(部分)解决这个问题。语义上来说,optional 代表一个“也许有效”“可选”的对象。语法上来说,一个 optional 对象有点像一个指针,但它所管理的对象是直接放在 optional 里的,没有额外的内存分配。

构造一个 optional<T> 对象有以下几种方法:

不传递任何参数,或者使用特殊参数 std::nullopt(可以和 nullptr 类比),可以构造一个“空”的 optional 对象,里面不包含有效值。

第一个参数是 std::in_place,后面跟构造 T 所需的参数,可以在 optional 对象上直接构造出 T 的有效值。

如果 T 类型支持拷贝构造或者移动构造的话,那在构造 optional<T> 时也可以传递一个 T 的左值或右值来将 T 对象拷贝或移动到 optional 中。

对于上面的第 1 种情况,optional 对象里是没有值的,在布尔值上下文里,会得到 false(类似于空指针的行为)。对于上面的第 2、3 两种情况,optional 对象里是有值的,在布尔值上下文里,会得到 true(类似于有效指针的行为)。类似的,在 optional 对象有值的情况下,你可以用 * 和 -> 运算符去解引用(没值的情况下,结果是未定义行为)。

虽然 optional 是 C++17 才标准化的,但实际上这个用法更早就通行了。因为 optional 的实现不算复杂,有些库里就自己实现了一个版本。比如 cpptoml [3] 就给出了下面这样的示例(进行了翻译和重排版),用法跟标准的 optional 完全吻合:

auto val = config->

get_as<int64_t>("my-int");

// val 是 cpptoml::option<int64_t>

if (val) {

// *val 是 "my-int" 键下的整数值

} else {

// "my-int" 不存在或不是整数

}

cpptoml 里只是个缩微版的 optional,实现只有几十行,也不支持我们上面说的所有构造方式。标准库的 optional 为了方便程序员使用,除了我目前描述的功能,还支持下面的操作:

安全的析构行为

显式的 has_value 成员函数,判断 optional 是否有值

value 成员函数,行为类似于 *,但在 optional 对象无值时会抛出异常 std::bad_optional_access

value_or 成员函数,在 optional 对象无值时返回传入的参数

swap 成员函数,和另外一个 optional 对象进行交换

reset 成员函数,清除 optional 对象包含的值

emplace 成员函数,在 optional 对象上构造一个新的值(不管成功与否,原值会被丢弃)

make_optional 全局函数,产生一个 optional 对象(类似 make_pair、make_unique 等)

全局比较操作

等等

如果我们认为无值就是数据无效,应当跳过剩下的处理,我们可以写出下面这样的高阶函数:

template <typename T>

constexpr bool has_value(

const optional<T>& x) noexcept

{

return x.has_value();

}

template <typename T,

typename... Args>

constexpr bool has_value(

const optional<T>& first,

const optional<

Args>&... other) noexcept

{

return first.has_value() &&

has_value(other...);

}

template <typename F>

auto lift_optional(F&& f)

{

return [f = forward<F>(f)](

auto&&... args) {

typedef decay_t<decltype(f(

forward<decltype(args)>(args)

.value()...))>

result_type;

if (has_value(args...)) {

return optio
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员zhi路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值