函数可以应用于 std::optional,并返回一个可选值吗?



我认为将函数应用于可选是一种非常有用的模式。然而,使用C++STL很麻烦。例如:

std::optional<Vector3D> vec = tryCreateVector();
std::optional<float> length = 
vec.has_value() ? std::optional<float>(vec->length()) : std::nullopt;

在C++中是否有相当于哈斯克尔fmap或鲁斯特Option::map的等价物?如下所示:

std::optional<Vector3D> vec = tryCreateVector();
std::optional<float> length = map(vec, [](auto vec) { return vec.length(); });

您可以定义以下函数:

namespace detail
{
template<typename Callable, typename T>
struct apply_helper
{
using T_noref = typename std::remove_reference<T>::type;
using value_type = typename T_noref::value_type;
using Callable_return = decltype(std::declval<Callable>()(std::declval<value_type>()));
using return_type = optional<Callable_return>;
static return_type eval(Callable&& f, T&& val)
{
if(val)
{
return apply(std::forward<Callable&&>(f), *val);
}
else return boost::none;
}
private:
static Callable_return apply(Callable&& f, value_type& v)
{
return f(v);
}
static Callable_return apply(Callable&& f, value_type const& v)
{
return f(v);
}
static Callable_return apply(Callable&& f, value_type&& v)
{
return f(v);
}
};
}
template<typename Callable, typename T> 
optional<decltype(std::declval<Callable>()(std::declval<T>()))> apply(Callable&& f, optional<T> const& a)
{
return detail::apply_helper<Callable, optional<T> const&>::eval(std::forward<Callable>(f), a);
}

然后可以像这样使用:

optional<int> foo(optional<int> value)
{
auto f = [](int v){return v + 10;};
return apply(f, value);
}

据我所知,标准库不提供开箱即用的功能。不过,实现起来相当容易。

#include <optional>
#include <iostream>
#include <functional>
std::optional<int> create_an_int()
{
return 1;
}
std::optional<int> dont_create_an_int()
{
return {};
}
template<typename T, typename F>
auto handler_wrapper(const std::optional<T>& in, F h)
{
return in.has_value() ? std::optional{h(*in)} : std::nullopt;
}
int main()
{
auto handler = [](const int& in){ return 3*in; };
auto test = create_an_int();
auto multiplied = handler_wrapper(test, handler);
std::cout << *multiplied << std::endl;
test = dont_create_an_int();
auto nulled = handler_wrapper(test, handler);
if (!nulled.has_value())
std::cout << "null optional" << std::endl;
}

基本上你需要做的就是创建一个接受任何可调用和可选的模板包装器,你就完成了(注意:上面的代码片段不是最漂亮/最好的实现,但我想应该给你一个很好的起点(。 上面的代码显然会产生"3"和"null可选"作为输出。

最新更新