从参数包中排除前n个参数

时间:2017-07-18 16:06:54

标签: c++ c++14 metaprogramming variadic-templates variadic-functions

我有一个函数leal,它调用函数foo,其中包含传递给bar的可变参数模板的类型子集。例如:

foo

有没有办法提取“子参数包”。在上面的例子中 如果template <typename... T> void foo() { // ... template <size_t start_idx, typename... T> using param_pack = /*Parameter pack with T[start_idx]...T[N]*/ auto b = bar<param_pack<2, T...>>(); // ... } T = [int float char double]

[编辑]

我的目标是能够使用类似的东西来匹配事件处理程序。例如

param_pack<2, T...> = [char double]

这里struct ev {}; template <typename... T> struct event : ev { std::tuple<T...> data_; event(T&&... d) : data_(std::make_tuple(std::forward<T>(d)...)) {} }; template <typename... Functor> struct handler { std::tuple<Functor...> funcs_; handler(Functor&&... f) : funcs_(std::make_tuple(std::forward<Functor>(f)...)) {} void handle_message(ev* e) { auto ptrs = std::make_tuple( dynamic_cast<event<param_pack<1, typename function_traits<F>::args>>*>(e)... ); match(ptrs); } }; 获取函数参数的参数包,并匹配元组function_traits::args上的迭代,检查funcs_是否成功并执行第一个成功函数。我已经实现了这些。

处理程序类似于

dynamic_cast

我基本上试图摆脱[] (handler* self, <ARGS>) -> void { // ... } 论点。

3 个答案:

答案 0 :(得分:3)

为了简单起见,它没有对索引N进行检查这一事实,这里有一个基于函数声明(无需定义)和使用声明的可能解决方案:

template<std::size_t N, typename... T, std::size_t... I>
std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>
sub(std::index_sequence<I...>);

template<std::size_t N, typename... T>
using subpack = decltype(sub<N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));

这种方法的好的部分是你不必引入围绕元组设计的新类型,然后以某种方式迭代地专门化它。

它遵循使用上述代码的最小工作示例:

#include<functional>
#include<tuple>
#include<cstddef>
#include<type_traits>

template<std::size_t N, typename... T, std::size_t... I>
std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>
sub(std::index_sequence<I...>);

template<std::size_t N, typename... T>
using subpack = decltype(sub<N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));

int main() {
    static_assert(std::is_same<subpack<2, int, float, char, double>, std::tuple<char, double>>::value, "!");
}

wandbox上查看并运行完整示例。

包含对索引N的检查的扩展版本如下所示:

template<std::size_t N, typename... T, std::size_t... I>
std::enable_if_t<(N < sizeof...(T)), std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>>
sub(std::index_sequence<I...>);

这是您在std::enable_if_t中包含的第一个示例中可以看到的类型,仅此而已。同样,声明就足够了,不需要定义。

修改

如果您想使用自己的类模板而不是std::tuple,可以轻松修改代码来执行此操作:

#include<functional>
#include<tuple>
#include<cstddef>
#include<type_traits>

template<typename...>
struct bar {};

template<template<typename...> class C, std::size_t N, typename... T, std::size_t... I>
std::enable_if_t<(N < sizeof...(T)), C<std::tuple_element_t<N+I, std::tuple<T...>>...>>
sub(std::index_sequence<I...>);

template<template<typename...> class C, std::size_t N, typename... T>
using subpack = decltype(sub<C, N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));

int main() {
    static_assert(std::is_same<subpack<bar, 2, int, float, char, double>, bar<char, double>>::value, "!");
}

修改

根据问题中添加的代码,上述解决方案仍然有效。您应该按照以下方式定义event类:

struct ev {};

template <typename>
struct event;

template <typename... T>
struct event<std::tuple<T...>>: ev {
    // ...
};

这样,当你这样做时:

event<param_pack<1, typename function_traits<F>::args>>

你仍然可以从param_pack中获取一个元组(在我的例子中是subpack使用声明),但它匹配event的模板部分特化,参数包是您的处置为T...

这是您可以做的最好的,因为您不能将参数包放在using声明中。无论如何它只是起作用,所以可能它可以解决你的问题。

答案 1 :(得分:2)

您可以执行以下操作:

template <std::size_t N, typename ... Ts> struct drop;

template <typename ... Ts>
struct drop<0, Ts...>
{
    using type = std::tuple<Ts...>;
};

template <std::size_t N, typename T, typename ... Ts>
struct drop<N, T, Ts...>
{
    using type = typename drop<N - 1, Ts...>;
};

// Specialization to avoid the ambiguity
template <typename T, typename... Ts>
struct drop<0, T, Ts...>
{
    using type = std::tuple<T, Ts...>;
};

答案 2 :(得分:0)

这是一个快速但不是特别可重复使用的解决方案。

template <typename Pack, std::size_t N, std::size_t... Is>
void invoke_bar_impl(std::index_sequence<Is...>) {
    bar<std::tuple_element_t<N + Is, Pack>...>();
}

template <std::size_t N, typename... Ts>
void invoke_bar() {
    auto indices = std::make_index_sequence<sizeof...(Ts) - N>();
    invoke_bar_impl<std::tuple<Ts...>, N>(indices);
}