问题
If I have std::tuple<double, double, double>
(where the type is homogeneous), is there a stock function or constructor to convert to std::array<double>
?
Edit:: I was able to get it working with recursive template code (my draft answer posted below). Is this the best way to handle this? It seems like there would be a stock function for this... Or if you have improvements to my answer, I'd appreciate it. I'll leave the question unanswered (after all, I want a good way, not just a workable way), and would prefer to select someone else's [hopefully better] answer.
Thanks for your advice.
回答1:
Converting a tuple to an array without making use of recursion, including use of perfect-forwarding (useful for move-only types):
#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:
You can do it non-recursively:
#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());
}
See https://gitlab.com/redistd/redistd/blob/master/include/redi/index_tuple.h for my implementation of index_tuple, something like that is useful for working with tuples and similar variadic templates. A similar utility was standardised as std::index_sequence
in C++14 (see index_seq.h for a standalone C++11 implementation).
回答3:
I would return the array instead of populating it by reference, so that auto
can be used to make the callsite cleaner:
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);
Realistically, NRVO will eliminate most performance concerns.
回答4:
The C++17 solution is a short one:
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));
}
Use it as
auto tup = std::make_tuple(1.0,2.0,3.0);
auto arr = get_array_from_tuple(tup);
EDIT: forgot to sprinkle constexpr
anywhere :-)
回答5:
#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;
}
回答6:
Even if title says C++11 I think C++14 solution is worth sharing (since everyone searching for problem will come up here anyway). This one can be used in compile time (constexpr
proper) and much shorter than other solutions.
#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;
}
Example
回答7:
In C++14 you can do this to generate an array:
auto arr = apply([](auto... n){return std::array<double, sizeof...(n)>{n...};}, tup);
Full code:
#include<experimental/tuple> // apply
#include<cassert>
//using std::experimental::apply; c++14 + experimental
using std::apply; // c++17
template<class T, class Tuple>
auto to_array(Tuple&& t){
return apply([](auto... n){return std::array<T, sizeof...(n)>{n...};}, t); // c++14 + exp
}
int main(){
std::tuple<int, int, int> t{2, 3, 4};
auto a = apply([](auto... n){return std::array<int, 3>{n...};}, t); // c++14 + exp
assert( a[1] == 3 );
auto a2 = apply([](auto... n){return std::array<int, sizeof...(n)>{n...};}, t); // c++14 + exp
assert( a2[1] == 3 );
auto a3 = apply([](auto... n){return std::array{n...};}, t); // c++17
assert( a3[1] == 3 );
auto a4 = to_array<int>(t);
assert( a4[1] == 3 );
}
来源:https://stackoverflow.com/questions/10604794/convert-stdtuple-to-stdarray-c11