如何从一个 T 类型的数组创建一个包含 N 个 T 元素的元组?

12

如果我有一个constexpr整数数组,长度为N,我该如何将它转换成适当的constexpr std::tuple<...>


2
你的数组是原始数组还是std::array或其他类型? - Cheers and hth. - Alf
1
当你已经有一个数组时,为什么还需要一个元组? - Barry
2个回答

18

这里有一种可能的原始数组实现:

#include<functional>

template<std::size_t... I, std::size_t N>
constexpr auto f(const int (&arr)[N], std::index_sequence<I...>) {
    return std::make_tuple(arr[I]...);
}

template<std::size_t N>
constexpr auto f(const int (&arr)[N]) {
    return f(arr, std::make_index_sequence<N>{});
}

int main() {
    constexpr int arr[] = { 0, 1, 2 };
    constexpr auto tup = f(arr);
    static_assert(std::get<0>(tup) == 0, "!");
    static_assert(std::get<1>(tup) == 1, "!");
    static_assert(std::get<2>(tup) == 2, "!");
}
constexpr数组的大小可以在编译时推断,因此您不必显式指定大小。该大小可用于内部创建一组索引,以从数组中获取元素并即时创建元组。 如评论中所述,如果要更一般化并接受原始数组和std::array,可以这样做:
#include<functional>
#include<array>

template<std::size_t... I, typename U>
constexpr auto f(const U &arr, std::index_sequence<I...>) {
    return std::make_tuple(arr[I]...);
}

template<typename T, std::size_t N>
constexpr auto f(const T (&arr)[N]) {
    return f(arr, std::make_index_sequence<N>{});
}

template<typename T, std::size_t N>
constexpr auto f(const std::array<T, N> &arr) {
    return f(arr, std::make_index_sequence<N>{});
}

int main() {
    constexpr int arr1[] = { 0, 1, 2 };
    constexpr auto tup1 = f(arr1);
    static_assert(std::get<0>(tup1) == 0, "!");
    static_assert(std::get<1>(tup1) == 1, "!");
    static_assert(std::get<2>(tup1) == 2, "!");

    constexpr std::array<int, 3> arr2 = { 0, 1, 2 };
    constexpr auto tup2 = f(arr2);
    static_assert(std::get<0>(tup2) == 0, "!");
    static_assert(std::get<1>(tup2) == 1, "!");
    static_assert(std::get<2>(tup2) == 2, "!");
}

1
我喜欢这个答案,它有一个更清晰/简洁的界面。谢谢! - Short
我将你的答案与我的结合起来,以涵盖基本数组和std::array<T, N>。 - Short
@短时间来说,我不会像你对std::array那样做。如果你愿意,我可以更新我的回答来涵盖它们。 - skypjack
谢谢,我能够根据那个使我的代码整洁多了。 - Short
@Short 为什么你要复制另一个答案来集成你的?这没有太多意义。它们不必完全相同。 - skypjack

2
将数组转换为元组利用了std::integer sequence的优势,在编译时通过调用辅助函数来建立数组索引。
以下是演示此过程的代码。 http://coliru.stacked-crooked.com/a/b2d6c6ca1f5dc635
////////////////////////////////////////////////////////////////////////////////////////////////////
// tuple_from_array
namespace detail {
template<typename T, std::size_t... Is>
auto constexpr tuple_from_array(T const& arr, std::index_sequence<Is...>)
{
  return std::make_tuple(arr[Is]...);
}

template<std::size_t N, typename V, typename T, std::size_t ...Is>
auto constexpr array_from_container(T const& c, std::index_sequence<Is...>)
{
  return std::array<V, N>{c[Is]...};
}

} // ns detail

template<typename T>
auto constexpr tuple_from_array(T const& arr)
{
  auto constexpr tup_size = std::tuple_size<std::decay_t<T>>::value;
  return detail::tuple_from_array(arr, std::make_index_sequence<tup_size>{});
}

template<typename T, std::size_t N>
auto constexpr tuple_from_array(T const (&arr)[N])
{
  return detail::tuple_from_array(arr, std::make_index_sequence<N>{});
}

// not safe
template<std::size_t N, typename T>
auto constexpr tuple_from_container(T const& c)
{
  using V = typename T::value_type;
  return tuple_from_array(detail::array_from_container<N, V>(c, std::make_index_sequence<N>{}));
}

编辑:我将skypjack@的答案与我的答案结合起来,以回答评论中提出的基本数组问题。不过我需要等两天才能将其作为自己的答案再次发布 :(

2
发布有帮助的问答是受到鼓励的。但“代码转储”作为答案并不代表优质。我建议您编辑带上解释。 - StoryTeller - Unslander Monica
这个在运行时怎么会有“零开销”呢? - Kerrek SB
constexpr函数在编译时将数组转换为元组。抱歉,我更新了示例和代码链接以将元组实例化为constexpr值。 - Short
将内部细节作为模板参数向用户公开并不是一个好的做法:如果该参数存在,用户一定会开始使用和滥用它。这在这里也是不必要的,因为您可以使用index_sequence_for来代替。 - Kerrek SB
@简述:很抱歉,“index_sequence_for”在这里没有用处。但是您应该将大小计算直接粘贴到调用站点中,而不是将其作为公共模板参数。如果您喜欢,可以使用中间的私有模板,但似乎并没有必要。 - Kerrek SB
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接