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

Vit*_*meo 8 c++ templates metaprogramming template-meta-programming c++17

上下文

首先,一些背景:我使用的是空的struct所谓nothing效仿类似的东西"常规void"为了美化一些接口依靠链接多个函数对象在一起.

struct nothing { };
Run Code Online (Sandbox Code Playgroud)

用法示例:

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

在上面的例子中,所发生的情况是,我包装传递给函数的对象的所有结果when_all中的std::tuple,转换voidnothing (在这个例子中:std::tuple<int, nothing, char>),然后我用被称为辅助函数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
Run Code Online (Sandbox Code Playgroud)

apply_ignoring_nothing是以实施的方式实施的call_ignoring_nothing.


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

template <typename F, typename... Ts>
constexpr decltype(auto) call_ignoring_nothing(F&& f, Ts&&... xs);
Run Code Online (Sandbox Code Playgroud)

此函数将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>;
Run Code Online (Sandbox Code Playgroud)

我实现的方式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));
}
Run Code Online (Sandbox Code Playgroud)

如果通过lambda ,递归的情况需要f,xxs...,并且有条件地绑定x为一个f参数!is_nothing_v<decltype(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)...);
}
Run Code Online (Sandbox Code Playgroud)

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

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

max*_*x66 6

从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))...)
   );
 }
Run Code Online (Sandbox Code Playgroud)

以下是一个工作示例

#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    
 }
Run Code Online (Sandbox Code Playgroud)

wandbox上的实例