繁体   English   中英

在编译时迭代地过滤与谓词匹配的参数

[英]Iteratively filtering arguments matching a predicate at compile-time

上下文

首先,一些上下文:我使用一个名为nothing的空struct来模拟类似于“常规void ”的东西,以便美化一些依赖于将多个函数对象链接在一起的接口。

struct nothing { };

用法示例:

when_all([]{ return 0; }, []{ }, []{ return 'a'; })
    .then([](int, char){ }); // result of lambda in the middle ignored

在上面的例子中,所发生的情况是,我包装传递给函数的对象的所有结果when_allstd::tuple ,将voidnothing (在这个例子: std::tuple<int, nothing, char> ),然后我使用一个名为apply_ignoring_nothing的辅助函数,它通过解apply_ignoring_nothing std::tuple来调用一个函数对象,忽略那些nothing是的元素。

auto f_then = [](int, char){ };
auto args = std::tuple{0, nothing{}, 'a'};
apply_ignoring_nothing(f_then, args); // compiles

apply_ignoring_nothing是根据call_ignoring_nothing


我有一个函数call_ignoring_nothing具有以下签名:

template <typename F, typename... Ts>
constexpr decltype(auto) call_ignoring_nothing(F&& f, Ts&&... xs);

此函数将通过完美转发所有xs...来调用f xs...编译时is_nothing_v<T>返回false

is_nothing_v定义如下:

template <typename T>
inline constexpr bool is_nothing_v = std::is_same_v<std::decay_t<T>, nothing>;

我实现call_ignoring_nothing的方式是递归的 基本情况只需要f并简单地调用它:

#define FWD(x) ::std::forward<decltype(x)>(x)

template <typename F>
constexpr decltype(auto) call_ignoring_nothing(F&& f)
{
    return returning_nothing_instead_of_void(FWD(f));
}

递归情况需要fxxs... ,如果!is_nothing_v<decltype(f)>通过lambda,则有条件地将x绑定为f的参数之一。 然后通过call_ignoring_nothing递归传递新创建的lambda作为f

template <typename F, typename T, typename... Ts>
constexpr decltype(auto) call_ignoring_nothing(F&& f, T&& x, Ts&&... xs)
{
    return call_ignoring_nothing(
        [&](auto&&... ys) -> decltype(auto) {
            if constexpr(is_nothing_v<T>)
            {
                return FWD(f)(FWD(ys)...);
            }
            else
            {
                return FWD(f)(FWD(x), FWD(ys)...);
            }
        },
        FWD(xs)...);
}

我想以迭代的方式实现call_ignoring_nothing ,可能使用包扩展来过滤掉参数而不递归。

是否可以在没有递归的情况下实现call_ignoring_nothing 我想不出任何允许在包扩展期间过滤出参数的技术。

与Griwes的建议没有什么不同但是...我想你可以使用std::apply()std::tuple_cat()std::get()和元组,它们是空的或者是根据is_nothing_v的值。

我的意思是......像[编辑:改进TC的建议和OP本身的例子(Vittorio Romeo)]

template <bool B, typename ... Ts>
constexpr auto pick_if (Ts && ... xs)
 {
   if constexpr ( B ) 
      return std::forward_as_tuple(std::forward<Ts>(xs)...);
   else
      return std::tuple{};
 }

template <typename F, typename ... Ts>
constexpr decltype(auto) call_ignoring_nothing (F && f, Ts && ... xs)
 {
   return std::apply(f,
      std::tuple_cat(pick_if<!is_nothing_v<Ts>>(std::forward<Ts>(xs))...)
   );
 }

以下是一个工作示例

#include <tuple>
#include <iostream>
#include <type_traits>

struct nothing { };

template <typename T>
constexpr bool is_nothing_v = std::is_same<std::decay_t<T>, nothing>::value;

template <bool B, typename ... Ts>
constexpr auto pick_if (Ts && ... xs)
 {
   if constexpr ( B )
      return std::forward_as_tuple(std::forward<Ts>(xs)...);
   else
      return std::tuple{};
 }

template <typename F, typename ... Ts>
constexpr decltype(auto) call_ignoring_nothing (F && f, Ts && ... xs)
 {
   return std::apply(f,
      std::tuple_cat(pick_if<!is_nothing_v<Ts>>(std::forward<Ts>(xs))...)
   );
 }

float foo (int a, float b) { return a + b; }

int main ()
 {
   std::cout << call_ignoring_nothing(foo, nothing{}, 12, nothing{},
      2.3f, nothing{}); // print 14.3    
 }

wandbox上的实例

这是另一个不依赖于tuple_cat 首先通过“普通”constexpr函数模板计算一包bools为true位置:

template<class... Bools>
constexpr int count(Bools... bs) 
{
    return (bool(bs) + ...);
}

template<bool... bs>
constexpr std::array<std::size_t, count(bs...)> indices() 
{
    std::array<std::size_t, count(bs...)> ret = {};
    std::size_t i = 0, j = 0;
    for(bool b : {bs...}) {
        if(b) {
            ret[j] = i;
            ++j;
        }
        ++i;
    }
    return ret;
}

然后将结果转换为index_sequence

template<bool...bs, std::size_t...Is>
constexpr auto indices_as_sequence_helper(std::index_sequence<Is...>) 
{ 
    return std::index_sequence<indices<bs...>()[Is]...>{}; 
}

template<bool...bs>
constexpr auto indices_as_sequence() 
{ 
    return indices_as_sequence_helper<bs...>(std::make_index_sequence<count(bs...)>()); 
}

然后这是一个简单的事情: forward_as_tuple + get index_sequence

template <typename F, typename... Ts, std::size_t... Is>
constexpr decltype(auto) call_some(std::index_sequence<Is...>, F&& f, Ts&&... xs)
{
    return std::forward<F>(f)(
               std::get<Is>(std::forward_as_tuple(std::forward<Ts>(xs)...))...);
}

template <typename F, typename... Ts>
constexpr decltype(auto) call_ignoring_nothing(F&& f, Ts&&... xs)
{
    return call_some(indices_as_sequence<!is_nothing_v<Ts>...>(), 
                     std::forward<F>(f), std::forward<Ts>(xs)...);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM