Search code examples
c++stlstdoptional

Can a function be applied to the value of a std::optional, getting back an optional?


I think that applying a function to an optional is a really useful pattern. It is however cumbersome to do with the C++ STL. For example:

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

Is there an equivalent of haskell's fmap or rust's Option::map in C++? Something like the following:

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

Solution

  • You could define the following function:

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

    which could then be used like:

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