首页 文章

当返回类型为void时,使用其他lambda结果调用lambda

提问于
浏览
4

以下函数使lambda使用第一个callable的结果调用第二个callable . 如果第一个callable返回一个元组,它将应用于第二个callable .

template<typename T>
struct is_tuple : std::false_type{};

template<typename... T>
struct is_tuple<std::tuple<T...>> : std::true_type{};

template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
    return [callables = std::tuple<S, T>(std::forward<S>(source), std::forward<T>(target))]
    (auto&&... args)
    {
        const auto&[source, target] = callables;

        using source_return = decltype(source(args...));

        if constexpr(is_tuple<source_return>::value)
        {
            return std::apply(target, source(std::forward<decltype(args)>(args)...));
        }
        else
        {
            return target(source(std::forward<decltype(args)>(args)...));
        }
    };
}

但是,当源可调用返回void时,这不会编译,因为它将尝试调用具有不完整类型void的目标,因此我尝试了以下操作:

template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
    return [callables = std::tuple<S, T>(std::forward<S>(source), std::forward<T>(target))]
    (auto&&... args)
    {
        const auto&[source, target] = callables;

        using source_return = decltype(source(args...));

        if constexpr(is_tuple<source_return>::value)
        {
            return std::apply(target, source(std::forward<decltype(args)>(args)...));
        }
        else if constexpr(std::is_void_v<source_return>)
        {
            source(std::forward<decltype(args)>(args)...);
            return target();
        }
        else
        {
            return target(source(std::forward<decltype(args)>(args)...));
        }
    };
}

但这似乎并不起作用,因为它始终与void分支相同,即使源函数在任何情况下都不能返回void . 我认为推断source_return的decltype有问题 . 我试图将source的结果分配给一个变量来decltype那个变量而不是decltype(source(args ...))但是它给了我一个错误,即变量是不完整的类型void在它实际返回的情况下无效,所以我必须在实际调用source之前检查它 .

以下是无法编译的管道使用示例:

auto callable = pipeline([]{ return 10 },
                         [](size_t val){ return val * 10});

callable();

它不编译的原因是因为由于某种原因它使得source_return与void branch相同 . 任何人都知道如何用args调用源代码的返回类型...以更强大的方式?

编辑:

我通过使用call_pipeline辅助函数让它工作 . 我仍然不明白为什么这个会起作用而另一个不起作用 .

template<typename S, typename T, typename... Args>
constexpr decltype(auto) call_pipeline(const S& source, const T& target, Args&&... args)
{
    using source_return = decltype(source(std::forward<Args>(args)...));

    if constexpr(std::is_void_v<source_return>)
    {
        source(std::forward<Args>(args)...);
        return target();
    }
    else
    {
        if constexpr(is_tuple<source_return>::value)
        {
            return std::apply(target, source(std::forward<Args>(args)...));
        }
        else
        {
            return target(source(std::forward<Args>(args)...));
        }
    }
}

template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source_init, T&& target_init)
{
    return [callables = std::tuple<S, T>(std::forward<S>(source_init),
                                         std::forward<T>(target_init))]
    (auto&&... args)
    {
        const auto&[source, target] = callables;
        return call_pipeline(source, target, std::forward<decltype(args)>(args)...);
    };
}

1 回答

  • 0

    不确定是不是按你的方式工作,但我建议以下替代方案

    template<typename S, typename T>
    constexpr decltype(auto) pipeline(S&& source, T&& target)
    {
        return [callables = std::tuple<S, T>(std::forward<S>(source),
                                             std::forward<T>(target))]
        (auto&&... args)
        {
            const auto&[source, target] = callables;
    
            auto tplr = [](auto s, auto && ... as)
            {
               using source_return
                  = decltype(s(std::forward<decltype(as)>(as)...));
                if constexpr ( is_tuple<source_return>::value )
                    return s(std::forward<decltype(as)>(as)...);
                else if constexpr ( std::is_void_v<source_return> )
                {
                    s(std::forward<decltype(as)>(as)...);
                    return std::make_tuple();
                }
                else
                    return std::make_tuple(s(std::forward<decltype(as)>(as)...));
            }(source, std::forward<decltype(args)>(args)...);
    
            std::apply(target, tplr);
        };
    }
    

    这个想法永远是 target() 通过 std::apply 调用 std::tuple (可能是空的)参数 .

相关问题