You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现支持std::expected的通用magic_apply?C++23有现成特性吗?

C++23中简化std::expected错误检查的方案

问题背景

现有如下C++23代码:

std::expected<A, std::string> getA(const X& x);
std::expected<B, std::string> getB(const Y& y);

C compute_all(const A& a, const B& b);

传统的错误检查写法需要逐个判断std::expected的状态,代码冗余:

auto a_ret = getA(x);
if (!a_ret)
  return a_ret.error();

auto b_ret = getB(y);
if (!b_ret)
  return b_ret.error();

C final_ret = compute_all(*a_ret, *b_ret);

希望简化为类似如下的形式:

std::expected<C, std::string> final_ret = magic_apply(compute_all, getA(x), getB(y));

已有一个仅支持两个std::expected参数的magic_apply实现,但需要更通用的版本——支持变长模板,且允许向compute_all传入非std::expected类型的参数:

template<typename Func, typename A, typename B, typename Err>
auto magic_apply(Func func, const std::expected<A, Err>& a, const std::expected<B, Err>& b)
-> std::expected<decltype(func(a.value(), b.value())), Err>
{
    if(!a) {
        return std::unexpected{ a.error() };
    }
    if(!b) {
        return std::unexpected{ b.error() };
    }
    return func(a.value(), b.value());
}

提问:C++语言中是否已有可直接实现该功能的内置特性?


回答

C23本身没有提供直接对应这种"批量解包std::expected并短路处理错误"的内置特性,但可以基于C23的现有语言特性(变长模板、折叠表达式、类型特征、完美转发等)实现通用版的magic_apply,满足你的需求:

通用版magic_apply实现

#include <expected>
#include <type_traits>
#include <utility>

namespace detail {
    // 提取参数值:非expected类型直接转发,expected类型取内部值
    template <typename T>
    constexpr decltype(auto) get_value(T&& val) noexcept {
        return std::forward<T>(val);
    }

    template <typename T, typename E>
    constexpr decltype(auto) get_value(std::expected<T, E>&& exp) {
        return std::move(exp).value();
    }

    template <typename T, typename E>
    constexpr decltype(auto) get_value(const std::expected<T, E>& exp) {
        return exp.value();
    }

    template <typename T, typename E>
    constexpr decltype(auto) get_value(std::expected<T, E>& exp) {
        return exp.value();
    }

    // 检查所有expected参数,返回第一个错误
    template <typename Err>
    constexpr std::expected<void, Err> check_errors() noexcept {
        return {};
    }

    template <typename Err, typename First, typename... Rest>
    constexpr std::expected<void, Err> check_errors(const First& first, const Rest&... rest) {
        if constexpr (std::is_same_v<std::decay_t<First>, std::expected<typename First::value_type, Err>>) {
            if (!first) {
                return std::unexpected{first.error()};
            }
        }
        return check_errors<Err>(rest...);
    }

    // 从参数列表推导错误类型(取第一个expected的错误类型)
    template <typename... Args>
    struct error_type_from_args {
        using type = void;
    };

    template <typename T, typename E, typename... Rest>
    struct error_type_from_args<std::expected<T, E>, Rest...> {
        using type = E;
    };

    template <typename T, typename E, typename... Rest>
    struct error_type_from_args<const std::expected<T, E>&, Rest...> {
        using type = E;
    };

    template <typename T, typename E, typename... Rest>
    struct error_type_from_args<std::expected<T, E>&, Rest...> {
        using type = E;
    };

    template <typename T, typename E, typename... Rest>
    struct error_type_from_args<std::expected<T, E>&&, Rest...> {
        using type = E;
    };

    template <typename... Args>
    using error_type_from_args_t = typename error_type_from_args<Args...>::type;
}

template <typename Func, typename... Args>
constexpr auto magic_apply(Func&& func, Args&&... args) {
    using ErrType = detail::error_type_from_args_t<Args...>;

    // 无expected参数时,直接调用函数并返回结果
    if constexpr (std::is_same_v<ErrType, void>) {
        return std::forward<Func>(func)(std::forward<Args>(args)...);
    } else {
        // 先检查所有expected参数,遇到第一个错误立即返回
        auto err_check = detail::check_errors<ErrType>(args...);
        if (!err_check) {
            return std::expected<decltype(detail::get_value(std::forward<Args>(args))...), ErrType>(
                std::unexpected{err_check.error()}
            );
        }

        // 所有参数有效,调用函数并包装为expected返回
        return std::expected{
            std::forward<Func>(func)(detail::get_value(std::forward<Args>(args))...)
        };
    }
}

实现特性说明

  • 变长参数支持:可接受任意数量的参数,无需限制个数
  • 混合参数类型:同时支持std::expected和普通类型参数,普通参数会直接转发给目标函数
  • 短路错误处理:遍历参数时遇到第一个错误的std::expected就立即返回该错误,不会继续处理后续参数
  • 完美转发:保持参数的左值/右值属性,避免不必要的拷贝
  • 自动类型推导:自动从第一个std::expected参数推导错误类型,无需手动指定

内容的提问来源于stack exchange,提问作者Teolazza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 06:12:05