【问题标题】:Exclude first n arguments from parameter pack从参数包中排除前 n 个参数
【发布时间】:2017-12-23 15:09:41
【问题描述】:

我有一个函数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&lt;2, T...&gt; = [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 {
  // ...
}

我实际上是在尝试摆脱 self 参数。

【问题讨论】:

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


【解决方案1】:

为了简单起见,它缺少对索引N 的检查这一事实,这里有一个基于函数声明(不需要定义)和 using 声明的可能解决方案:

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 using 声明),但它与event 的模板部分特化匹配,并且参数包可供您使用T... .

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

【讨论】:

  • 类型是std::tuple&lt;T...&gt;有没有办法直接获取包,即T...
  • @subzero 您不能将包放在 using 声明中。您所能做的就是将要填充的类型与其他数据一起传递并取回而不是元组。它对你有用吗?你想怎么用那个包?
  • @subzero 我添加了另一个示例来展示如何使用自己的类模板。让我知道它是否适合您。
  • 我在问题中添加了一个示例使用
  • @subzero function_traits::args 是您要存储的类型列表吗?
【解决方案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...>;
};

【讨论】:

  • 我试图实现类似的想法,但坚持编译器无法为N = 0 选择专业化这一事实。请您看一下:wandbox.org/permlink/u3QOECdxokexLgyG
  • @EdgarRokyan:确实,需要额外的专业化。 Fixed version.
  • 完美,谢谢!虽然现在看起来有点尴尬:(
  • 类型是std::tuple&lt;T...&gt;有没有办法直接获取包,即T...
  • 不是using。但是之后您仍然可以轻松地将std::tuple&lt;Ts...&gt; 更改为bar&lt;Ts...&gt;
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 2017-02-27
    • 1970-01-01
    • 2023-03-30
    • 2019-07-07
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-13
    相关资源
    最近更新 更多