Подтвердить что ты не робот

Преобразовать std:: tuple в std:: array С++ 11

Если у меня есть std::tuple<double, double, double> (где тип является однородным), существует ли функция запаса или конструктор для преобразования в std::array<double>?

Изменить:: мне удалось заставить его работать с рекурсивным кодом шаблона (мой проект ответа размещен ниже). Это лучший способ справиться с этим? Похоже, что для этого будет функция запаса... Или, если у вас есть улучшения в моем ответе, я буду признателен. Я оставлю вопрос без ответа (в конце концов, я хочу хороший способ, а не просто работоспособный способ), и предпочел бы выбрать кого-то еще [надеюсь, лучшего ответа].

Спасибо за ваш совет.

4b9b3361

Ответ 1

Преобразование кортежа в массив без использования рекурсии, включая использование совершенной пересылки (полезно для типов только для перемещения):

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

template<int... Indices>
struct indices {
    using next = indices<Indices..., sizeof...(Indices)>;
};

template<int Size>
struct build_indices {
    using type = typename build_indices<Size - 1>::type::next;
};

template<>
struct build_indices<0> {
    using type = indices<>;
};

template<typename T>
using Bare = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

template<typename Tuple>
constexpr
typename build_indices<std::tuple_size<Bare<Tuple>>::value>::type
make_indices()
{ return {}; }

template<typename Tuple, int... Indices>
std::array<
  typename std::tuple_element<0, Bare<Tuple>>::type,
    std::tuple_size<Bare<Tuple>>::value
>
to_array(Tuple&& tuple, indices<Indices...>)
{
    using std::get;
    return {{ get<Indices>(std::forward<Tuple>(tuple))... }};
}

template<typename Tuple>
auto to_array(Tuple&& tuple)
-> decltype( to_array(std::declval<Tuple>(), make_indices<Tuple>()) )
{
    return to_array(std::forward<Tuple>(tuple), make_indices<Tuple>());
}

int main() {
  std::tuple<double, double, double> tup(1.5, 2.5, 4.5);
  auto arr = to_array(tup);
  for (double x : arr)
    std::cout << x << " ";
  std::cout << std::endl;
  return 0;
}

Ответ 2

Вы можете сделать это нерекурсивно:

#include <array>
#include <tuple>
#include <redi/index_tuple.h>  // see below

template<typename T, typename... U>
  using Array = std::array<T, 1+sizeof...(U)>;

template<typename T, typename... U, unsigned... I>
  inline Array<T, U...>
  tuple_to_array2(const std::tuple<T, U...>& t, redi::index_tuple<I...>)
  {
    return Array<T, U...>{ std::get<I>(t)... };
  }

template<typename T, typename... U>
  inline Array<T, U...>
  tuple_to_array(const std::tuple<T, U...>& t)
  {
    using IndexTuple = typename redi::make_index_tuple<1+sizeof...(U)>::type;
    return tuple_to_array2(t, IndexTuple());
  }

См. https://gitlab.com/redistd/redistd/blob/master/include/redi/index_tuple.h для моей реализации index_tuple, что-то вроде этого полезно для работы с кортежами и аналогичными вариативными шаблонами. Аналогичная утилита была стандартизирована как std::index_sequence в С++ 14 (см. index_seq.h для автономной реализации С++ 11).

Ответ 3

Я бы вернул массив вместо того, чтобы заполнить его ссылкой, так что auto можно использовать для очистки очистителя:

template<typename First, typename... Rem>
std::array<First, 1+sizeof...(Rem)>
fill_array_from_tuple(const std::tuple<First, Rem...>& t) {
  std::array<First, 1+sizeof...(Rem)> arr;
  ArrayFiller<First, decltype(t), 1+sizeof...(Rem)>::fill_array_from_tuple(t, arr);
  return arr;
}

// ...

std::tuple<double, double, double> tup(0.1, 0.2, 0.3);
auto arr = fill_array_from_tuple(tup);

Реально NRVO устранит большинство проблем с производительностью.

Ответ 4

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

template<class First, class Tuple, std::size_t N, std::size_t K = N>
struct ArrayFiller {
  static void fill_array_from_tuple(const Tuple& t, std::array<First, N> & arr) {
    ArrayFiller<First, Tuple, N, K-1>::fill_array_from_tuple(t, arr);
    arr[K-1] = std::get<K-1>(t);
  }
};

template<class First, class Tuple, std::size_t N>
struct ArrayFiller<First, Tuple, N, 1> {
  static void fill_array_from_tuple( const Tuple& t, std::array<First, N> & arr) {
    arr[0] = std::get<0>(t);
  }
};

template<typename First, typename... Rem>
void fill_array_from_tuple(const std::tuple<First, Rem...>& t, std::array<First, 1+sizeof...(Rem)> & arr) {
  ArrayFiller<First, decltype(t), 1+sizeof...(Rem)>::fill_array_from_tuple(t, arr);
}

int main() {
  std::tuple<double, double, double> tup(0.1, 0.2, 0.3);
  std::array<double, 3> arr;
  fill_array_from_tuple(tup, arr);

  for (double x : arr)
    std::cout << x << " ";
  return 0;
}

Ответ 5

Даже если название говорит о С++ 11, я думаю, что решение С++ 14 стоит использовать (так как все, кто ищет проблему, все равно приходят сюда). Это можно использовать во время компиляции (constexpr proper) и намного короче, чем другие решения.

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

// Convert tuple into a array implementation
template<typename T, std::size_t N, typename Tuple,  std::size_t... I>
constexpr decltype(auto) t2a_impl(const Tuple& a, std::index_sequence<I...>)
{
        return std::array<T,N>{std::get<I>(a)...};
}

// Convert tuple into a array
template<typename Head, typename... T>
constexpr decltype(auto) t2a(const std::tuple<Head, T...>& a)
{
        using Tuple = std::tuple<Head, T...>;
        constexpr auto N = sizeof...(T) + 1;
        return t2a_impl<Head, N, Tuple>(a, std::make_index_sequence<N>());
}

int main()
{
    constexpr auto tuple = std::make_tuple(-1.3,2.1,3.5);
    constexpr auto array = t2a(tuple);

    static_assert(array.size() == 3, "err");

    for(auto k : array)
        std::cout << k << ' ';
    return 0;
}

Пример

Ответ 6

Решение С++ 17 является коротким:

template<typename tuple_t>
constexpr auto get_array_from_tuple(tuple_t&& tuple)
{
    constexpr auto get_array = [](auto&& ... x){ return std::array{std::forward<decltype(x)>(x) ... }; };
    return std::apply(get_array, std::forward<tuple_t>(tuple));
}

Используйте это как

auto tup = std::make_tuple(1.0,2.0,3.0);
auto arr = get_array_from_tuple(tup);

РЕДАКТИРОВАТЬ: забыл посыпать constexpr любом месте :-)