【问题标题】:How to take nested parameter packs in variadic templates?如何在可变参数模板中采用嵌套参数包?
【发布时间】:2017-11-25 05:30:13
【问题描述】:

如何将参数包中的每个参数表示为可变参数模板本身就是一个参数包?

考虑这段代码:

template <typename... TS>
void use_tuple(std::tuple<TS...> arg0);

template <typename... TS0, typename... TS1>
void use_tuple(std::tuple<TS0...> arg0, std::tuple<TS1...> arg1);

我希望use_tuple 能够获取任意数量的元组。现在我必须这样写:

template <typename... TS0, typename... REST>
void use_tuple(std::tuple<TS0...> arg0, REST... rest);

void use_tuple();  // Terminates the recursion.

但我想这样写:

// Each ELEMENT in PACK_OF_PACKS is a parameter pack.
template <(typename...)... PACK_OF_PACKS>
void use_tuple(std::tuple<PACK_OF_PACKS...>... args);

这甚至可能吗?如果是这样,怎么做?如果没有,我还能做什么?我对这段代码的目标是获取所有元组中包含的类型

我的最终目标是这样的:

template <typename...> void foo();

use_tuple(std::tuple<int, float, char>{},
          std::tuple<double, short, std::string>{},
          std::tuple<std::function<void()>, std::vector<int>>{});
// Results in a call to
// foo<int, float, char, double, short, std::string,
//     std::function<void()>, std::vector<int>>();

但我想在不依赖于传递的元组的数量或每个中的元素数量的少量恒定间接中实现这一点。所以没有递归。

【问题讨论】:

  • use_tuple(std::tuple&lt;PACK_OF_PACKS...&gt;... args) 的正文会是什么样子?它必须有一个 ... ... 扩展,这不是语言的一部分
  • @Filipp - 你的意思是use_tuple&lt;std::tuple&lt;std::tuple&lt;pack1...&gt;, std::tuple&lt;pack2...&gt;, std::tuple&lt;pack3...&gt;, ... 还是std::tuple&lt;pack1..., pack2..., pack3...
  • edit 澄清您希望对元组做什么。有关上下文,请参阅this
  • 你使用的是 C++11 还是 C++14?
  • 任一标准的可用性会改变答案吗?我很高兴看到任何 C++ 版本(11 或更高版本)的答案。

标签: c++ c++11 templates c++14 variadic-templates


【解决方案1】:

如果没有,我还能做什么?

为什么不直接将元组本身作为Tuples... 传递,然后在use_tuple 的主体中提取类型?

template <typename...>
using example = int;

template <typename... Ts>
void foo(Ts...) { }

template <typename... Tuples>
void use_tuple(Tuples...)
{
    foo(typename types<Tuples>::template apply<example>{}...);
}

您可以使用帮助器 types 类提取类型:

template <typename Tuple>
struct types;

template <typename... Ts>
struct types<std::tuple<Ts...>>
{
    template <template <typename...> class T>
    using apply = T<Ts...>;
};

live example on wandbox

【讨论】:

  • 嘿,维托里奥!虽然我喜欢你的想法,但我认为它只是将问题从一个地方转移到另一个地方。而不是use_tuple 采用N 个元组,现在foo 采用N 个实例化示例。我面临着完全相同的困境。
  • @Filipp:你最后想对元组做什么?您最终将不得不提取和扩展包
  • 是的,这就是我的目标。但我的问题是如何一次性做到这一点,而不是一次递归地分解一个元组。
【解决方案2】:

我编写此代码的目标是获取所有元组中包含的类型。

不确定您是否希望所有这些都作为一个独特的包。 PACK_OF_PACKS 实际上建议它。在这种情况下,您可以使用std::tuple_cat
沿着这条线的东西应该可以工作:

template<typename... T>
void do_use_tuple(std::tuple<T...>) {
    // T are the types you are looking for
}

template <typename... T>
void use_tuple(T&&... tuple) {
    return do_use_tuple(std::tuple_cat(std::forward<T>(tuple)...));
}

如果您对转发值不感兴趣,只需使用decltype 即可轻松获取。

更多详情请见here


如果您不想将所有参数打包在一起,并且希望一次探索一个元组,则可以改为执行以下操作:

template<typename T>
struct tag { using type = T; };

template<typename... T, typename F>
void use_one(std::tuple<T...> &&, F f) {
    F(tag<T>{}...);
}

template <typename... T>
void use_tuple(T&&... tuple) {
    int arr = { 0,
        (use_one(std::forward<T>(tuple), [](auto... tag) {
            // tag::type contains the types of each
            // parameter of a tuple, a tuple at a time
       }), 0)...
    };
    (void)arr;
}

如果您还想访问这些值,可以轻松调整 lambda 和涉及的其他函数。

【讨论】:

  • skypjack,我知道tuple_cat,但是我看到的所有实现都是通过一次递归分解一两个元组来实现的。我想避免这样做,而是让编译器推断一次推断所有参数包。请参阅 Vittorio 回答下的 cmets。
  • @Filipp 好吧,带有 lambda 的示例不会递归地执行此操作。
【解决方案3】:

我猜你的想法类似于以下 (C++17)。让我强调一下我不推荐它。显然应该更喜欢std::tuple_cat

http://coliru.stacked-crooked.com/a/c64f2cb9a79af3e2

#include <array>
#include <iostream>
#include <tuple>
#include <utility>

////////////////////////////////////////////////////////////////////////////////

template<class... Ts>
void foo() {
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

////////////////////////////////////////////////////////////////////////////////

namespace detail {

struct Index {
  std::size_t outer{};// identify the tuple
  std::size_t inner{};// identify the element in the tuple
};

template<std::size_t... ns, class... Tuples>
constexpr void use_tuple(std::index_sequence<ns...>, Tuples...) {
  constexpr auto inds = [&] () {
    std::array<Index, sizeof...(ns)> inds{};
    std::size_t n = 0;

    for(std::size_t outer=0; outer<sizeof...(Tuples); ++outer) {
      std::size_t size = std::begin({std::tuple_size<Tuples>::value...})[outer];
      for(std::size_t inner=0; inner<size; ++inner) inds[n++] = {outer, inner};
    }

    return inds;
  }();

  using TupleTuple = std::tuple<Tuples...>;

  foo<
    std::tuple_element_t<
      inds[ns].inner,
      std::tuple_element_t<inds[ns].outer, TupleTuple>
    >...
  >();
}

}// detail

template<class... Tuples>
constexpr void use_tuple(Tuples... tuples) {
  constexpr std::size_t N = (std::tuple_size<Tuples>{} + ...);
  detail::use_tuple(std::make_index_sequence<N>{}, std::move(tuples)...);
}

////////////////////////////////////////////////////////////////////////////////

int main() {
  std::tuple<int, unsigned, int> t0;
  std::tuple<double, float> t1;
  std::tuple<char, bool, bool, int> t2;

  use_tuple(t0, t1, t2);

  return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-01
    • 2017-08-06
    • 2016-12-01
    • 1970-01-01
    • 2012-04-07
    • 1970-01-01
    相关资源
    最近更新 更多