【问题标题】:Call lambda with other lambda result when its return type is void当其返回类型为 void 时,使用其他 lambda 结果调用 lambda
【发布时间】:2018-09-10 05:59:03
【问题描述】:

以下函数创建一个 lambda,它使用第一个可调用对象的结果调用第二个可调用对象。如果第一个 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。我猜 decltype 推断 source_return 有问题。我试图将 source 的结果分配给一个变量来 decltype 那个变量而不是 decltype(source(args...)) 但是它给了我一个错误,即在它实际返回的情况下,该变量是不完整类型的 void void,所以我必须在实际调用 source 之前检查它。

这是一个不编译的管道使用示例:

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

callable();

它不编译的原因是由于某种原因它需要 source_return 与 void 分支相同。任何人都知道如何以更健壮的方式在使用 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)...);
    };
}

【问题讨论】:

  • is_tuple 长什么样子?
  • 添加了 is_tuple 特征
  • 那你还需要展示你是如何使用pipeline
  • 即使我定义了using source_return = int;,我仍然会在return target(); 上抛出错误。很奇怪。

标签: c++ lambda metaprogramming c++17


【解决方案1】:

不确定是否行不通,但我建议以下替代方案

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

这个想法是通过std::apply 调用target() 并带有std::tuple(可能是空的)参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-04
    • 2020-01-19
    • 2018-06-18
    • 1970-01-01
    • 1970-01-01
    • 2017-06-10
    • 2022-01-14
    • 2020-06-14
    相关资源
    最近更新 更多