2017-02-27 34 views
6

W prostej bibliotece parsera, którą piszę, wyniki wielu parserów są łączone przy użyciu std::tuple_cat. Ale przy stosowaniu parsera, który wielokrotnie zwraca ten sam wynik, ważne staje się przekształcenie tej krotki w kontener, taki jak wektor lub deque.C++ Przekształć std :: tuple <A, A, A...> na std :: vector lub std :: deque

Jak można tego dokonać? W jaki sposób można przekonwertować dowolną krotkę tego typu na std::vector<A>?

Myślę, że to może być możliwe przy użyciu typename ...As i sizeof ...(As), ale nie jestem pewien, jak utworzyć mniejszą krotkę, aby wywołać funkcję rekursywnie. Albo jak napisać rozwiązanie iteracyjne, które wyodrębnia elementy z krotki jeden po drugim. (jako std::get<n>(tuple) jest skonstruowany podczas kompilacji).

Jak to zrobić?

+0

Prawdopodobny duplikat [iteracji przez krotkę] (http://stackoverflow.com/questions/1198260/iterate-over-tuple) – filmor

+1

Zupełnie inny. Znacznie lepiej podchodzi się do [sztuczka wskazówka] (http://loungecpp.wikidot.com/tips-and-tricks:indices). – Xeo

Odpowiedz

3

Oto jeden ze sposobów, aby to zrobić:

#include <tuple> 
#include <algorithm> 
#include <vector> 
#include <iostream> 

template<typename first_type, typename tuple_type, size_t ...index> 
auto to_vector_helper(const tuple_type &t, std::index_sequence<index...>) 
{ 
    return std::vector<first_type>{ 
     std::get<index>(t)... 
      }; 
} 

template<typename first_type, typename ...others> 
auto to_vector(const std::tuple<first_type, others...> &t) 
{ 
    typedef typename std::remove_reference<decltype(t)>::type tuple_type; 

    constexpr auto s = 
     std::tuple_size<tuple_type>::value; 

    return to_vector_helper<first_type, tuple_type> 
     (t, std::make_index_sequence<s>{}); 
} 

int main() 
{ 
    std::tuple<int, int> t{2,3}; 

    std::vector<int> v=to_vector(t); 

    std::cout << v[0] << ' ' << v[1] << ' ' << v.size() << std::endl; 
    return 0; 
} 
12

Wraz z wprowadzeniem std::apply(), to jest bardzo proste:

template <class Tuple, 
    class T = std::decay_t<std::tuple_element_t<0, std::decay_t<Tuple>>>> 
std::vector<T> to_vector(Tuple&& tuple) 
{ 
    return std::apply([](auto&&... elems){ 
     return std::vector<T>{std::forward<decltype(elems)>(elems)...}; 
    }, std::forward<Tuple>(tuple)); 
} 

std::apply() jest C++ 17 funkcji, ale jest możliwe do wdrożenia w C + +14 (patrz link do ewentualnej implementacji). Jako ulepszenie możesz dodać SFINAE lub static_assert, że wszystkie typy w Kodzie są w rzeczywistości T.


Jako T.C. Zwraca uwagę, to ponosi dodatkową kopię każdego elementu, ponieważ std::initializer_list jest wspierany przez const tablicy. To niefortunne. Wygrywamy niektóre z nich, nie musząc sprawdzać granic każdego elementu, ale tracimy trochę na kopiowaniu. Kopiowanie kończy się zbyt drogie, alternatywą byłaby realizacja:

template <class Tuple, 
    class T = std::decay_t<std::tuple_element_t<0, std::decay_t<Tuple>>>> 
std::vector<T> to_vector(Tuple&& tuple) 
{ 
    return std::apply([](auto&&... elems) { 
     using expander = int[]; 

     std::vector<T> result; 
     result.reserve(sizeof...(elems)); 
     expander{(void(
      result.push_back(std::forward<decltype(elems)>(elems)) 
      ), 0)...}; 
     return result; 
    }, std::forward<Tuple>(tuple)); 
} 

Zobacz this answer o wyjaśnienie sztuczki ekspandera. Zauważ, że odrzuciłem wiodącą 0, ponieważ wiemy, że paczka nie jest pusta. Z C++ 17, to staje się czystsze z rozkładanym wypowiedzi:

return std::apply([](auto&&... elems) { 
     std::vector<T> result; 
     result.reserve(sizeof...(elems)); 
     (result.push_back(std::forward<decltype(elems)>(elems)), ...); 
     return result; 
    }, std::forward<Tuple>(tuple)); 

Choć nadal stosunkowo nie tak ładna jak konstruktora initializer_list. Niefortunny.

+0

Plus 1 nie tylko dla wspaniałej odpowiedzi, ale przede wszystkim dla porady twierdzącej. –

+0

Jedyne co mi się nie podoba to lista inicjalizatorów, która jest jedną dodatkową kopią na element. –

+0

@ T.C. Och, masz rację. Nie myślałem o tym. To niefortunne ... Chyba istnieje 'reserve()'/'push_back()'? – Barry

Powiązane problemy