C++23 添加了一些关于可选值的“一元式”功能,作为
optional<T>
: 的方法
optional<T>::and_then()
(忽略 this
的限定词):
template<class F> constexpr auto and_then(F&& f);
退货 对包含的值调用 f 的结果(如果存在)。 否则,返回返回类型的空值。
optional<T>::transform()
(忽略 this
的限定词):
template<class F> constexpr auto transform(F&& f);
如果
包含值,则返回std::optional
,其中包含对所包含值调用f
的结果。否则,返回此类类型的空*this
。std::optional
那么,这两个函数不是做同样的事情吗?
假设您有一个
optional<T1>
值。
transform()
可让您将选项传递给 T2 foo(T1 x)
;and_then()
可让您将选项传递给 optional<T2> bar(T1 x)
;...最后得到一个
optional<T2>
。因此,transform()
会将函数的输出“重新装箱”为可选值,而and_then()
将期望函数自行返回装箱值。
您可能还会认为
transform
就像 std::transform
:您将一个函数应用于“每个元素”;在容器中,它可以是任意数量的元素,在可选元素中,它可以是 0 或 1 个元素。
另请参阅这个问题。
and_then
仅接受 T -> std::optional<U>
类型的函数(而 transform
可以自由接受返回任何类型的函数)。
如果您只是
transform
使用这样的功能,您将得到一个std::optional<std::optional<U>>
。
and_then
然后将std::optional<std::optional<U>>
压平成std::optional<U>
。
这就是所有单子:
transform
由类型级别 flatten
组成。想想range<range<U>>
和future<future<U>>
。
and_then
是单子 bind
又名 flatmap
又名 >>=
transform
是函子 map
。
一般可以用
map
来表达 bind
,但反之则不然,因为函子不一定是单子。当然, std::optional
的特定 monad 可以随时打开,因此这两个函数都可以用普通的 C++23 之前的 std::optional
API 来表达。因此,为什么 C++ 标准定义这两个函数的问题并不比为什么它定义这两个函数的问题更好。也许标准希望为程序员提供一个独立的标准函数接口和标准单子接口。这两个界面本身都是有用且重要的。