【问题标题】:Can a function be applied to the value of a std::optional, getting back an optional?可以将函数应用于 std::optional 的值,取回一个可选值吗?
【发布时间】:2018-08-24 12:16:16
【问题描述】:

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

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

在 C++ 中是否有等效于 haskell 的 fmap 或 rust 的 Option::map?类似于以下内容:

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

【问题讨论】:

  • 不在标准中。我正在编写一个提供这些东西的库。

标签: c++ stl stdoptional


【解决方案1】:

你可以定义如下函数:

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);
}

【讨论】:

  • boost 的引用是C++ 17 之前的旧代码的残余吗?
  • 是的,我用 boost::optional 做了原型。对于std::optional,您将使用std::nullopt
【解决方案2】:

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

#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;
}

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

【讨论】:

    猜你喜欢
    • 2017-05-14
    • 1970-01-01
    • 2012-01-02
    • 2011-02-04
    • 2016-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多