【问题标题】:Produce std::tuple of same type in compile time given its length by a template argument给定模板参数的长度,在编译时生成相同类型的 std::tuple
【发布时间】:2016-12-17 13:06:18
【问题描述】:

在 c++ 中,如何实现一个带有指示元组长度的 int 模板参数的函数并生成具有该长度的 std::tuple?

例如

func<2>() returns std::tuple<int, int>();
func<5>() returns std::tuple<int, int, int, int, int>().

【问题讨论】:

  • 到目前为止你尝试了什么?
  • 强制:您是否考虑过只使用std::array?您可以像元组一样使用array(使用std::tuple_sizestd::get 等),这会大大简化。
  • @user2079303,我的直觉是我们保留一个类型列表,从 N 倒数到 0 并在计数器递减时将 int 添加到列表中。当 N 为 0 时,返回带有类型列表的元组。但我不知道如何执行。
  • @RyanHaining,我正在调用一个只接受 std::tuple 的函数。
  • @Fake 我不知道您是否是该函数的作者,但通常使用元组作为具体类型(即:不在通用上下文中)是糟糕的设计。如果可以,尝试重新设计函数以接受支持std::get 等的“TupleLike”概念。

标签: c++ c++11 template-meta-programming stdtuple


【解决方案1】:

这是一个带有别名模板的递归解决方案,它可以在 C++11 中实现:

template <size_t I,typename T> 
struct tuple_n{
    template< typename...Args> using type = typename tuple_n<I-1, T>::template type<T, Args...>;
};

template <typename T> 
struct tuple_n<0, T> {
    template<typename...Args> using type = std::tuple<Args...>;   
};
template <size_t I,typename T>  using tuple_of = typename tuple_n<I,T>::template type<>;

例如,如果我们想要"tuple of 3 doubles",我们可以这样写:

tuple_of<3, double> t;

【讨论】:

  • 这实际上比 Ryan 的解决方案 IMO 更好,即使对于 C++14 也是如此,因为它更通用 - 例如,您可以使用它声明变量。
【解决方案2】:

使用index_sequence 和辅助类型别名,您可以生成所需的类型:

// Just something to take a size_t and give the type `int`
template <std::size_t>
using Integer = int;

// will get a sequence of Is = 0, 1, ..., N
template <std::size_t... Is>
auto func_impl(std::index_sequence<Is...>) {
    // Integer<Is>... becomes one `int` for each element in Is...
    return std::tuple<Integer<Is>...>{};
}

template <std::size_t N>
auto func() {
    return func_impl(std::make_index_sequence<N>{});
}

值得一提的是,在一般情况下,std::array 可能会更好,(在你的情况下,你不能使用一个),但是 std::array 可以像元组一样,类似于std::pair.

更新:由于您已经明确表示您使用的是 c++11 而不是 14+,因此您需要从某个地方获得 index_sequence 和相关的实现(here 是 libc++ 的)。这是带有显式返回类型的funcfunc_impl 的C++11 版本:

template <std::size_t... Is>
auto func_impl(std::index_sequence<Is...>) -> std::tuple<Integer<Is>...> {
  return std::tuple<Integer<Is>...>{};
}

template <std::size_t N>
auto func() -> decltype(func_impl(std::make_index_sequence<N>{})) {
  return func_impl(std::make_index_sequence<N>{});
}

【讨论】:

  • 请注意,这是 C++14。
  • 可以用c++11实现吗?
  • @Fake 是的,如果您可以获得index_sequence 的实现,然后将显式返回类型添加到函数中。你的问题应该被标记为c++11吗?
  • @RyanHaining,添加了标签。抱歉,我以为默认排除了 c++14。
  • 你知道我怎样才能明确返回类型吗?
【解决方案3】:

普通的旧递归是你的朋友:

template<std::size_t N>
auto array_tuple() {
    return std::tuple_cat(std::tuple<int>{}, array_tuple<N-1>());
}

template<>
auto array_tuple<0>() {
    return std::tuple<>{};
}

【讨论】:

  • 对于auto函数的返回类型,指定需要C++14标准可能很有用。
【解决方案4】:

如果您对 C++14 解决方案感到满意,Ryan's answer 是您的最佳选择。

使用 C++11,您可以执行以下操作(仍然基于 index_sequence,但可以在 C++11 中实现):

template <size_t N, class T, class = std::make_index_sequence<N>>
struct n_tuple;

template <size_t N, class T, size_t... Is>
struct n_tuple<N, T, std::index_sequence<Is...>> {
    template <size_t >
    using ignore = T;

    using type = std::tuple<ignore<Is>...>;
};

template <size_t N, class T>
using n_tuple_t = typename n_tuple<N, T>::type;

这样:

template <size_t N>
n_tuple_t<N, int> func() {
    return n_tuple_t<N, int>{};
}

【讨论】:

  • 似乎 index_sequence 仍然是 c++14 功能。是否也可以不依赖它?
  • 或许我可以直接复制 index_sequence 的实现。
  • @Fake 是的,正如我在答案中所说,它可以在 C++11 中实现
【解决方案5】:

这里有两个 boost.hana 解决方案(C++14):

//first
hana::replicate<hana::tuple_tag>(int{}, hana::size_c<2>);

//second
hana::cycle(std::make_tuple(int{}), hana::size_c<2>);

两者都产生大小为 2 的整数元组,但它们产生的不是std::tuples,而是hana::tuples。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-11
    • 1970-01-01
    • 1970-01-01
    • 2018-08-07
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多