【发布时间】: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