CppCon 2015 学习:Improving the future<T> with monads
SolidFire 系统问题空间解析:
在 SolidFire 的数据存储架构中,以下是其工作流程和相关的关键挑战:
- 通过 iSCSI 请求数据:
- 数据请求通过 iSCSI 协议进入系统。这是一个用于连接存储设备并在网络上传输数据的协议。通过此协议,外部系统能够发出数据请求。
- 查找内部 ID:
- 系统会 查找内部 ID,这是一个映射过程,将外部请求的目标转换为内部存储中的唯一标识符。这样,系统才能精确地定位到数据所在的地方。
- 数据是否在本地缓存中:
- 本地缓存 是一个高速存储区域,用于存放最近或频繁访问的数据。如果数据已经存在于本地缓存中,那么响应速度会非常快。
- 从其他服务获取数据:
- 如果请求的数据不在本地缓存中,系统将从其他存储服务中 获取数据块。由于 SolidFire 是一个分布式系统,数据可能存储在不同的服务或节点上,系统需要协调从多个地方拉取数据。
- 用户可见的延迟:
- 用户看到的延迟是由整个请求链中 响应最慢的服务 决定的。如果其中任何一个服务(例如,缓存查找、数据块获取等)响应较慢,那么整个请求的延迟就会增加,从而影响最终用户的体验。
Travis Gockel (C++ Pirate SolidFire):
- Travis Gockel 是 SolidFire 的一位工程师或关键人物,可能是系统开发的核心成员之一。他的称号 “C++ Pirate” 暗示他在 C++ 编程方面有着深厚的专业背景,并且可能以一种幽默或俏皮的方式表现出他的技术特长。
关于元数据 (Metadata) 和编码 (encode()):
- 元数据:
- 元数据包含了很多关于数据存储的关键信息,比如 卷偏移量 (volumeoffset) 和 大小 (size) 等信息。这些元数据帮助 SolidFire 定位到具体的数据块位置,从而可以高效地管理和调取数据。
- 编码过程 (encode()):
encode()
可能是指在存储或传输数据时进行的 编码操作。在这个过程中,系统可能会对数据块或元数据进行一定的转换,以确保数据能够以合适的格式存储或者被其他服务理解。
总结:
- 数据请求流:通过 iSCSI 进入系统 → 查找内部 ID → 检查本地缓存 → 如果没有则从其他服务获取数据 → 受最慢响应服务影响的用户延迟。
- 系统效率的挑战:确保即使面对分布式存储和硬件故障,系统仍能快速、稳定地响应请求。
- 元数据和编码的作用:元数据帮助定位和管理数据块,编码过程确保数据按正确格式处理。
如果你对这部分内容有更深入的疑问或者需要进一步的解释,随时告诉我!
future<T>
的目标是什么?
future<T>
的目标是提供一种 异步通信机制,用于简化 并发编程。具体来说,它的作用是使得在并发程序中,任务的执行和结果的获取能够更加高效和清晰。
关键点:
- 异步通信:
future<T>
允许 提供者(promise) 和 接收者(future) 之间进行异步通信。也就是说,任务的执行和结果的获取是分开的,程序不需要等待任务完成才能继续执行其他操作。 - 提供者(promise):提供者是执行任务的部分,通常会发出一个任务,并返回一个
promise
,表示这个任务最终会提供一个结果。 - 接收者(future):接收者是等待结果的部分。它通过
future<T>
获取任务的最终结果,但不需要等待任务的完成,可以继续执行其他任务,等结果准备好时再进行处理。 - 简化并发编程:这种机制让并发编程变得更加容易,因为它提供了一种简单的方式来处理多个并行执行的任务,而不需要复杂的回调函数或线程同步机制。
总结:
future<T>
通过将任务分为执行和获取结果两个阶段,帮助程序员更容易地处理并发和异步操作。
希望这个解释清楚!如果有更多问题,随时问我。
std::future<T>
的介绍和目的
std::future<T> Purpose template <typename T>
class future {
public:T get();bool valid() const noexcept;void wait() const;template <typename TRep, typename TPeriod>future_status wait_for(const chrono::duration<TRep, TPeriod>& rel_time) const;template <typename TClock, typename TDuration>future_status wait_until(const chrono::time_point<TClock, TDuration>& abs_time) const;shared_future<T> share();
};
std::future<T>
是 C++ 中一个非常重要的类,它用于表示异步操作的结果。它允许程序在等待某个任务完成时,不阻塞当前线程,从而提高并发编程的效率。
目的:
std::future<T>
的主要目的是提供一种机制,能够获取异步任务的结果,并且允许程序在等待这些结果的同时继续执行其他操作。
std::future<T>
类的成员函数
1. T get()
:
- 作用:获取异步操作的结果。如果操作已经完成,
get()
会返回任务的结果;如果任务还没有完成,调用此函数会阻塞当前线程,直到结果准备好。 - 返回值:返回类型为
T
,即任务的结果类型。
2. bool valid() const noexcept
:
- 作用:检查
future
是否有效。如果任务已经完成并且结果可以被获取,返回true
;否则返回false
。 - 例子:如果
future
对象已经被移动或者没有与任何任务关联,则它是无效的。
3. void wait() const
:
- 作用:阻塞当前线程,直到异步任务完成并返回结果。如果任务已经完成,
wait()
立即返回;如果没有完成,它将一直等待,直到任务完成为止。
4. template <typename TRep, typename TPeriod> future_status wait_for(const chrono::duration<TRep, TPeriod>& rel_time) const
:
- 作用:在指定的时间内等待任务完成。如果在给定的时间限制内任务完成,返回
future_status::ready
;如果超时,则返回future_status::timeout
。 - 参数:
rel_time
是一个相对的时间段,用来指定最多等待的时间。 - 返回值:返回
future_status
枚举值(ready
、timeout
或deferred
)。
5. template <typename TClock, typename TDuration> future_status wait_until(const chrono::time_point<TClock, TDuration>& abs_time) const
:
- 作用:等待直到指定的绝对时间点。如果在给定时间点前任务完成,返回
future_status::ready
;如果任务超时,则返回future_status::timeout
。 - 参数:
abs_time
是一个绝对时间点,表示等待的最大时间点。 - 返回值:返回
future_status
枚举值(ready
、timeout
或deferred
)。
6. shared_future<T> share()
:
- 作用:返回一个
shared_future<T>
,这是一个能够共享的future
对象。shared_future
允许多个线程共享同一个任务结果。 - 使用场景:如果需要多个线程同时获取同一个异步任务的结果,可以使用
shared_future
来实现。
总结:
std::future<T>
提供了一个用于获取异步任务结果的机制,并且提供了多种方法来控制线程的行为,支持等待任务的完成和处理超时等情况。它是 C++ 中并发编程中的一个重要工具,帮助程序员更方便地处理异步操作。
如何使用 std::future<T>
?
在你提供的代码中,使用 std::future<T>
来进行 异步操作,以便并行地读取数据并合并结果。这是通过 std::async
来启动异步任务,并通过 future<T>
获取结果。
让我们逐步分析代码,并解释它的工作原理:
代码解析:
buffer read_multi(location x, location y) {// 启动一个异步任务来读取 x 位置的数据,并返回一个 future<buffer> 对象future<buffer> fut_z1 = async(&read, x);// 同时,在主线程中同步地读取 y 位置的数据buffer z2 = read(y);// 等待异步任务完成,获取异步任务的结果并与 z2 合并return fut_z1.get() + z2;
}
1. future<buffer> fut_z1 = async(&read, x);
- 作用:调用
std::async
函数启动一个异步任务。在这里,async
会启动一个新线程去执行read(x)
函数,并返回一个future<buffer>
对象fut_z1
。这个异步任务会在后台读取x
位置的数据,并将结果存储在fut_z1
对象中。 async
的工作原理:async
会异步执行函数(此处为read
)并立即返回一个future
对象。future
会表示一个异步操作的最终结果,可以通过.get()
方法获取结果。
2. buffer z2 = read(y);
- 作用:在主线程中同步读取
y
位置的数据。这是一个普通的同步操作,程序会等read(y)
完成并返回buffer z2
。
3. return fut_z1.get() + z2;
- 作用:调用
fut_z1.get()
获取异步任务的结果。.get()
会阻塞当前线程,直到fut_z1
中的任务完成并返回buffer
结果。获取结果后,将fut_z1.get()
和z2
合并并返回。 fut_z1.get()
:该方法会阻塞当前线程,直到异步任务完成并返回结果。如果异步任务已经完成,get()
会立即返回结果;如果任务还没完成,它将等待直到结果准备好。
then
函数的使用
在 C++ 中,then
是一种扩展 std::future<T>
的方式,用于在异步操作完成之后继续执行一个回调函数。这种方式实现了链式操作,即将异步操作和后续处理逻辑链接在一起。
1. then
函数的定义:
template <typename F>
auto then(F&& continuation) -> future<decltype(continuation(*this))>;
then
方法的作用是接收一个回调函数continuation
,并在当前future
完成时执行这个回调函数。then
会返回一个新的future
,这个future
包含的是回调函数执行后的结果。
关键点:
F&& continuation
:传入的回调函数continuation
是一个函数对象,可以是任何类型的可调用对象(如lambda
表达式、函数指针等)。decltype(continuation(*this))
:这是通过回调函数返回类型的推导,*this
代表当前future
对象的值。- 返回的
future<decltype(continuation(*this))>
类型表示回调函数执行后的结果,也就是说,then
返回一个新的future
,它将会存储回调的结果。
2. 示例代码解析:
future<int> x = async(&thing); // 启动异步操作,返回 future<int> x
future<double> y = x.then([](future<int> f) {return double(f.get()); // 从 future<int> 获取结果,并转换为 double
});
步骤解释:
- 启动异步任务:
async(&thing)
启动异步任务thing
,并返回一个future<int>
类型的x
,表示异步任务的结果类型是int
。
- 调用
then
:x.then(...)
表示在x
的异步操作完成之后执行一个新的回调函数。回调函数接受一个future<int>
类型的参数f
(这是x
对应的future
对象)。
- 回调函数的执行:
- 在回调函数内部,调用
f.get()
获取future<int>
对象f
中存储的值(即异步任务的返回值)。然后,将int
转换为double
类型并返回。
- 在回调函数内部,调用
- 返回新的
future<double>
:- 由于回调函数返回
double
类型的结果,then
方法会返回一个新的future<double>
类型的y
,表示x
完成后返回的转换结果。
- 由于回调函数返回
3. 如何工作:
- 链式操作:
then
允许将多个异步操作串联起来。在第一个异步操作完成后,then
会立即启动下一个异步操作。 - 传递上下文:在回调函数内部,我们可以使用
get()
获取前一个future
的结果,且不需要阻塞当前线程。 - 类型推导:
decltype(continuation(*this))
动态推导回调函数返回值的类型,以便返回正确类型的future
。
解析:Awkward for Aggregation
在 C++ 中,std::future<T>
用于表示异步任务的结果。你提到的代码示例展示了如何处理多个异步任务并收集它们的结果。让我们逐步解析这个问题。
代码解析:
std::vector<std::future<int>> all_tasks = foo(); // 获取多个异步任务
std::future<std::vector<std::future<int>>> all_done= when_all(make_move_iterator(begin(all_tasks)),make_move_iterator(end(all_tasks)));
1. std::vector<std::future<int>> all_tasks = foo();
:
- 这行代码通过调用
foo()
函数返回一个std::vector<std::future<int>>
。这个向量包含多个异步任务,每个任务的返回类型是int
。 - 假设
foo()
是一个函数,可能会生成多个异步任务,例如通过std::async
或其他方式。
2. when_all(...)
:
when_all
是一个函数,它用于 等待多个异步任务的完成。它接受一个迭代器范围(例如begin(all_tasks)
到end(all_tasks)
)并将这些异步任务传递给它。when_all
返回一个std::future<std::vector<std::future<int>>>
,这个future
对象包含了所有任务的future
结果。也就是说,all_done
是一个包含多个future<int>
对象的future
。
关键点:when_all
本质上是对一组异步任务的集合进行同步,它等到所有任务完成后,返回一个包含所有future
对象的向量。
3. make_move_iterator(begin(all_tasks)), make_move_iterator(end(all_tasks))
:
- 这部分代码使用了
make_move_iterator
来 移动 向量中的元素,而不是拷贝它们。这是为了避免不必要的拷贝,提升性能。all_tasks
中的每个future<int>
都被移动到when_all
函数中,而不是被拷贝。
哪个 future<T>::get
会阻塞?
在这个例子中,关键的问题是 哪个 future<T>::get()
会阻塞:
all_done.get()
会阻塞:all_done.get()
是唯一会真正阻塞的操作。为什么?因为all_done
是一个future<std::vector<std::future<int>>>
类型,它代表的是 所有异步任务的集合。当调用get()
时,它会等待 所有异步任务 都完成。all_done.get()
会阻塞,直到所有通过when_all
聚合的future<int>
都完成并返回结果。
future<T>::get()
的阻塞机制:- 每个
std::future<int>
都是一个异步任务的结果。当你调用get()
时,如果任务尚未完成,get()
会阻塞直到任务完成。 - 在这个场景中,当你访问
all_done
中的每个future<int>
时,get()
可能会阻塞,直到每个单独的任务完成。
- 每个
并发性和性能问题:
你提到了一些与性能相关的问题:
future<T>
是多线程的,必须处理锁:future<T>
本身是为多线程设计的,它的get()
函数可能会涉及到锁定机制,以确保在多线程环境下的安全访问。- 如果你在多个线程中并发调用
get()
,可能会引发 锁竞争,导致性能下降。
- 将
std::vector<int>
累加比std::vector<std::future<int>>
更快:- 处理
std::vector<int>
直接存储数据比处理std::vector<std::future<int>>
更高效。这是因为future<int>
是异步结果的表示,它本身需要额外的开销(例如锁、同步等),而直接操作int
值不会有这些开销。 - 如果你使用
std::vector<std::future<int>>
,每次调用get()
都会导致线程阻塞,等待异步任务完成,进而增加了额外的时间成本。 - 相反,如果你首先等待所有异步任务完成,然后将其结果存储到一个
std::vector<int>
中,再进行后续处理,通常会更加高效。
- 处理
如何优化:
- 减少
get()
的调用:- 可以通过批量获取所有
future<int>
的结果,而不是每个任务单独调用get()
,这样可以减少锁竞争。 - 一种优化方式是先使用
when_all
等待所有任务完成,然后批量处理结果,而不是逐个阻塞get()
。
- 可以通过批量获取所有
- 并发执行:
- 在设计并发任务时,尽量让任务能够并行执行,而不是让它们依赖于其他任务的结果。利用
when_all
聚合多个异步任务,确保在所有任务完成后再继续处理,避免中间阻塞。
- 在设计并发任务时,尽量让任务能够并行执行,而不是让它们依赖于其他任务的结果。利用
- 使用
std::shared_future
:- 如果需要多个地方读取同一个
future
的结果,可以使用std::shared_future
,它支持多个线程共享结果,而不需要重复阻塞。
- 如果需要多个地方读取同一个
总结:
when_all
用于等待多个异步任务完成,并返回一个包含所有任务结果的future
。all_done.get()
会阻塞,因为它会等待所有异步任务完成,才返回结果。- 处理
std::vector<int>
会比处理std::vector<std::future<int>>
更高效,避免了多次调用get()
的性能开销。 - 通过减少锁竞争、减少
get()
调用的频率,可以提高程序的并发性能。
代码解析与理解
你提供了两个代码片段,一个是嵌套的 try-catch
异常处理结构,另一个是使用 std::future
和 .then()
方法的异步链式操作。我们将逐步分析这两个代码片段,并讨论它们的差异和使用场景。
1. 嵌套的 try-catch
异常处理结构:
try {try {try {foo();} catch (...) {throw;}bar();} catch (...) {throw;}baz();
} catch (const std::exception& ex) {report(ex);
}
解析:
- 这段代码实现了多层嵌套的异常处理机制。每一层都有一个
try
块和一个catch
块,用来捕获异常。 - 最内层的
try-catch
:- 调用
foo()
,如果发生任何异常(catch(...)
捕获所有异常),会重新抛出该异常(throw;
)。不过这种重新抛出没有任何处理的意义,因为没有对异常进行任何修改或补充信息的操作。
- 调用
- 中间层的
try-catch
:- 捕获异常并重新抛出,与上一层类似。
- 外层的
try-catch
:- 调用
baz()
,如果发生异常,最终捕获该异常并执行report(ex)
来报告std::exception
类型的异常。
- 调用
问题:
- 冗余异常处理:每一层
catch
都只是将捕获的异常重新抛出,这样的处理方式没有任何实际意义。通常我们会在捕获异常后进行某些处理(比如日志记录、资源清理、或是特定的恢复操作),而这里的多重throw
并没有真正进行任何处理,导致代码冗长且没有实际作用。
优化:
- 将多层嵌套的
try-catch
简化为一个简单的结构,只需在外层捕获异常并进行报告,减少代码冗余。
2. 使用 std::future
和 .then()
的异步链式操作:
foo_async().then([](std::future<void> x) {x.get(); return bar();}).then([](std::future<void> y) {y.get(); return baz();}).then([](std::future<void> z) {try {z.get();} catch (const std::exception& ex) {report(ex);}});
解析:
- 这段代码展示了使用
std::future
进行异步操作,并通过.then()
来链接多个异步任务。每个then()
都是一个异步操作的后续步骤。
foo_async()
:foo_async()
是一个返回std::future<void>
的异步任务,它启动异步操作并返回一个future<void>
。
.then([](std::future<void> x) { x.get(); return bar(); })
:- 这是第一个
then()
链接的操作。它接受一个future<void>
对象x
,并调用x.get()
等待异步任务foo_async()
完成。 - 调用
x.get()
会阻塞当前线程直到foo_async()
完成,然后调用bar()
进行下一个操作。这里的return bar();
意味着返回bar()
的结果,它会被下一个then()
处理。
- 这是第一个
.then([](std::future<void> y) { y.get(); return baz(); })
:- 第二个
then()
会在bar()
完成后执行,等待上一个异步操作(bar()
)完成,接着执行baz()
。 - 同样的,它通过
y.get()
来等待前一个任务完成,之后继续执行baz()
。
- 第二个
.then([](std::future<void> z) { try { z.get(); } catch (const std::exception& ex) { report(ex); } })
:- 最后一个
then()
等待baz()
完成,并且包含异常处理。通过z.get()
获取baz()
的结果,若抛出异常,则通过catch (const std::exception& ex)
来捕获并报告异常。 - 这种结构确保了每一步的异常都能被捕获和报告。
- 最后一个
关键点:
- 链式调用:
std::future::then()
允许你将多个异步任务连接成一个链式结构。每个任务的完成都会触发下一个任务的执行。 get()
的使用:每个then()
中,调用get()
来等待异步任务完成,阻塞当前线程直到结果准备好。- 异常处理:最后一个
then()
中通过try-catch
捕获异常并调用report(ex)
来报告异常。这样,异常被集中处理,避免了冗余的异常捕获。
优势:
- 简洁且易于管理:将多个异步任务通过
.then()
连接成链式调用,避免了重复的异常处理和控制流管理,使代码更加清晰。 - 集中式异常处理:所有异步任务的异常处理都可以集中在最后一步
then()
中,避免了多次重复的catch
块。
对比与总结:
1. 多层嵌套的 try-catch
:
- 冗余:嵌套的
try-catch
结构在很多情况下会显得冗余,特别是当每一层catch
块只是简单地重新抛出异常时,实际并没有进行有效的错误处理。 - 不可扩展性:如果未来需要对每个异常类型做更细致的处理,嵌套的
try-catch
结构可能会变得复杂且难以维护。
2. 使用 std::future
和 .then()
进行异步链式调用:
- 简洁高效:通过
.then()
可以避免多重嵌套的异常处理,将异步任务和异常处理集中起来,更加简洁且易于扩展。 - 灵活性:你可以方便地增加、删除或调整异步任务的顺序,只需要修改
then()
链中的顺序,而不需要改变整个异常处理流程。 - 集中异常处理:异常处理通过
try-catch
集中在最后一个then()
中,确保异常只在最后处理,避免了代码的重复和冗余。
推荐做法:
- 对于异步任务,使用
std::future
和.then()
链式调用是一种更加现代和高效的方式,它不仅可以简化代码结构,还能提高代码的可维护性。 - 避免嵌套的
try-catch
,尤其是当它没有有效处理异常时。尽量减少不必要的异常重新抛出,集中处理异常,提升代码清晰度和效率。
解析:expected<T, E>
类型
在你提供的代码中,expected<T, E>
是一种表示异步任务结果的类型,它结合了两种概念:
- 异步通信 (
future<T>
):通过expected<T, E>
可以处理某个操作的异步结果。 - 错误报告:它不仅表示成功的结果,还能够处理和报告错误。
这个设计的灵感来自于函数式编程中的“单子(monad)”概念,特别是Option
或Either
类型。它允许你通过链式调用处理可能失败的操作,而无需显式地处理错误或异常。
1. expected<T, E>
类定义
template <typename T, typename E>
class expected {
public:auto status() const -> bool; // 检查操作是否成功auto get() -> T; // 获取成功的结果template <typename F> auto map(F f) -> expected<decltype(f(std::declval<T>()))>; // 映射操作template <typename F> auto catch_error(F f) -> expected<std::common_type_t<T, decltype(f(std::declval<std::exception_ptr>()))>>; // 错误处理
};
成员函数说明:
status()
:- 用于检查操作是否成功,返回
true
表示成功,false
表示失败。这个方法类似于检查std::future
的状态是否是有效的。
- 用于检查操作是否成功,返回
get()
:- 获取操作的成功结果。与
std::future<T>::get()
类似,get()
会返回存储的结果T
。如果失败,这可能会抛出错误。
- 获取操作的成功结果。与
map(F f)
:- 映射操作:接受一个函数
f
,并将该函数应用到当前值T
上。如果当前状态表示成功,那么返回一个新的expected
对象,包含映射后的结果。 - 这个方法的返回类型是
expected<decltype(f(std::declval<T>()))>
,即应用f
后的类型。
- 映射操作:接受一个函数
catch_error(F f)
:- 错误处理:如果当前状态表示失败(例如捕获到异常),它允许你通过函数
f
来处理错误。f
接受一个错误信息(例如std::exception_ptr
),并返回一个类型T
的值,作为错误处理的结果。 - 该方法的返回类型是一个新的
expected
对象,它的类型为expected<std::common_type_t<T, decltype(f(std::declval<std::exception_ptr>()))>>
,即错误处理后返回的类型。
- 错误处理:如果当前状态表示失败(例如捕获到异常),它允许你通过函数
2. 使用 expected<T, E>
的代码示例
expected<int> x = foo(); // foo() 返回一个期望值
expected<double> y = x.map([](int a) { return double(a); }); // 将 int 转换为 double
expected<double> z = y.catch_error([](auto) { return 0.0; }); // 错误时返回 0.0
expected<string> w = z.map([](double a) { return to_string(a); }); // 将 double 转换为 string
cout << w.get() << endl; // 输出最终结果
逐步解析:
expected<int> x = foo();
:- 调用
foo()
返回一个expected<int>
对象。这个对象代表一个异步操作的结果,成功时保存一个int
类型的值,失败时则包含一个错误信息。
- 调用
expected<double> y = x.map([](int a) { return double(a); });
:- 使用
map()
方法将x
中的int
值转换为double
。 - 如果
x
是成功的,map()
会将int
转换成double
,并返回一个新的expected<double>
对象。
- 使用
expected<double> z = y.catch_error([](auto) { return 0.0; });
:- 调用
catch_error()
来处理可能的错误。如果y
表示失败,那么错误处理函数[] { return 0.0; }
会返回0.0
作为错误的结果。 - 如果
y
成功,则catch_error()
不会做任何事情,直接返回y
。
- 调用
expected<string> w = z.map([](double a) { return to_string(a); });
:- 使用
map()
将double
转换为string
。 - 如果
z
成功,map()
将double
值转换为字符串,并返回一个新的expected<string>
。
- 使用
cout << w.get() << endl;
:- 最后调用
get()
来获取最终结果。如果所有操作都成功,w.get()
将返回一个string
,并打印到控制台。
- 最后调用
3. expected<T, E>
的优势
- 链式操作:你可以通过
map()
和catch_error()
等方法链式地组合多个操作,而无需显式地处理异常或错误。 - 错误处理:如果某个操作失败,
catch_error()
可以捕获错误并返回一个默认值或执行补救操作,从而避免程序崩溃。 - 函数式编程风格:
expected<T, E>
提供了类似于Option
或Either
类型的功能,支持将操作的结果与错误分开处理,从而使代码更加健壮和清晰。 - 类型安全:
expected<T, E>
通过将成功值和错误值明确区分,避免了直接使用null
或nullptr
,增加了类型安全性。
4. expected<T, E>
与 std::future<T>
的区别
std::future<T>
主要用于异步操作,通常表示任务的最终结果,但它不能直接处理错误信息,错误需要通过std::exception
或类似方式来捕获。expected<T, E>
不仅用于表示结果,还能明确地表示错误(例如,使用E
类型)。它提供了丰富的错误处理方法(如catch_error()
),可以在处理链式操作时轻松地捕获和处理错误。
5. 总结
expected<T, E>
提供了一种函数式编程风格的方式来处理异步结果和错误。通过 map()
进行操作转换,使用 catch_error()
捕获并处理错误,这种方式让你在处理异步任务时既能获取结果,也能优雅地处理错误,而不需要显式地使用 try-catch
或检查错误码。