2013-06-18 2 views
15

Wie kann ich die Typen in einem Tupel umkehren? Zum Beispiel möchte ich reverse_tuple<std::tuple<int, char, bool>>::typestd::tuple<bool, char, int> sein. Ich habe versucht, Folgendes zu tun, aber es hat nicht funktioniert. Was habe ich falsch gemacht?Wie storniere ich die Reihenfolge der Elementtypen in einem Tupel-Typ?

#include <type_traits> 
#include <tuple> 

template <typename... Ts> 
struct tuple_reverse; 

template <typename T, typename... Ts> 
struct tuple_reverse<std::tuple<T, Ts...>> 
{ 
    using type = typename tuple_reverse< 
          std::tuple< 
           typename tuple_reverse<std::tuple<Ts..., T>>::type 
          > 
          >::type; 
}; 

template <typename T> 
struct tuple_reverse<std::tuple<T>> 
{ 
    using type = std::tuple<T>; 
}; 

int main() 
{ 
    using result_type = std::tuple<int, bool, char>; 
    static_assert(
     std::is_same< 
      tuple_reverse<var>::type, std::tuple<char, bool, int> 
     >::value, "" 
    ); 
} 

Hier sind meine Fehler:

prog.cpp: In instantiation of ‘struct tuple_reverse<std::tuple<char, int, bool> >’:
prog.cpp:15:34: recursively required from ‘struct tuple_reverse<std::tuple<bool, char, int> >’
prog.cpp:15:34: required from ‘struct tuple_reverse<std::tuple<int, bool, char> >’
prog.cpp:29:31: required from here
prog.cpp:15:34: error: no type named ‘type’ in ‘struct tuple_reverse<std::tuple<int, bool, char> >’
prog.cpp: In function ‘int main()’:
prog.cpp:30:9: error: template argument 1 is invalid

+0

Ich glaube nicht, Sie Rekursion müssen dies tun, [tuple_cat] (http://www.cplusplus.com/reference/tuple/tuple_cat/), aber warum wollen Sie ein Tupel umkehren – aaronman

Antwort

15

Was du falsch gemacht hast war hier:

using type = typename tuple_reverse< 
         std::tuple< 
          typename tuple_reverse<std::tuple<Ts..., T>>::type 
         > 
         >::type; 

es von innen nach außen sucht, neu ordnen Sie die Tupelelemente: tuple<Ts..., T>, dann, dass Sie versuchen, rückgängig zu machen, dann setzen Sie das Ergebnis in einer tuple, dann versuchst du das umzukehren das ... huh ?! :)

Dies bedeutet jedes Mal, wenn Sie instanziieren tuple_reverse geben Sie ein Tupel der gleichen Größe, so dass es nie endet, und rekursiv instanziiert sich für immer. (Wenn diese Rekursion dann beendet ist, fügen Sie den resultierenden Tupel-Typ in ein Tupel ein, so dass Sie ein Ein-Element-Tupel mit einem N-Element-Tupel haben, und umgekehrt, was nichts bewirkt, weil das Umkehren eines Ein-Element-Tupels ein ist no-op)

Sie wollen eines der Elemente abzulösen, dann den Rest umkehren, und verketten es wieder zurück.

using head = std::tuple<T>; 
using tail = typename tuple_reverse<std::tuple<Ts...>>::type; 

using type = decltype(std::tuple_cat(std::declval<tail>(), std::declval<head>())); 

und Sie brauchen nicht in einem Tupel zu wickeln und wieder umkehren :)

Und Sie sollten auch mit dem leeren Tupel Fall umgehen, so ist die ganze Sache:

template <typename... Ts> 
struct tuple_reverse; 

template <> 
struct tuple_reverse<std::tuple<>> 
{ 
    using type = std::tuple<>; 
}; 

template <typename T, typename... Ts> 
struct tuple_reverse<std::tuple<T, Ts...>> 
{ 
    using head = std::tuple<T>; 
    using tail = typename tuple_reverse<std::tuple<Ts...>>::type; 

    using type = decltype(std::tuple_cat(std::declval<tail>(), std::declval<head>())); 
}; 

Ich würde es aber anders machen.

nur die Art zu erhalten, C++ 14

template<typename T, size_t... I> 
struct tuple_reverse_impl<T, std::index_sequence<I...>> 
{ 
    typedef std::tuple<typename std::tuple_element<sizeof...(I) - 1 - I, T>::type...> type; 
}; 

// partial specialization for handling empty tuples: 
template<typename T> 
struct tuple_reverse_impl<T, std::index_sequence<>> 
{ 
    typedef T type; 
}; 

template<typename T> 
struct tuple_reverse<T> 
: tuple_reverse_impl<T, std::make_index_sequence<std::tuple_size<T>::value>> 
{ }; 

, oder Sie können eine Funktion schreiben, ein aktuelles Tupel Objekt rückgängig zu machen, dann decltype(reverse(t)) die Art zu erhalten verwenden.Zum Umkehren eines Tupels-ähnliches Objekt in C++ 14:

template<typename T, size_t... I> 
auto 
reverse_impl(T&& t, std::index_sequence<I...>) 
{ 
    return std::make_tuple(std::get<sizeof...(I) - 1 - I>(std::forward<T>(t))...); 
} 

template<typename T> 
auto 
reverse(T&& t) 
{ 
    return reverse_impl(std::forward<T>(t), 
         std::make_index_sequence<std::tuple_size<T>::value>()); 
} 

In C++ 11 Verwendung <integer_seq.h> und Rückgabetypen hinzufügen und remove_reference auf Referenzen aus dem Tupel Typ Streifen (weil tuple_size und tuple_element nicht funktionieren mit Verweisen auf Tupel):

template<typename T, typename TT = typename std::remove_reference<T>::type, size_t... I> 
auto 
reverse_impl(T&& t, redi::index_sequence<I...>) 
-> std::tuple<typename std::tuple_element<sizeof...(I) - 1 - I, TT>::type...> 
{ 
    return std::make_tuple(std::get<sizeof...(I) - 1 - I>(std::forward<T>(t))...); 
} 

template<typename T, typename TT = typename std::remove_reference<T>::type> 
auto 
reverse(T&& t) 
-> decltype(reverse_impl(std::forward<T>(t), 
         redi::make_index_sequence<std::tuple_size<TT>::value>())) 
{ 
    return reverse_impl(std::forward<T>(t), 
         redi::make_index_sequence<std::tuple_size<TT>::value>()); 
} 
+0

Wie werde ich dabei so gut wie du? Ich hätte nie gedacht, es auf all diese erstaunlichen Weisen zu schreiben! :) –

+0

Üben, üben, üben. Nicht nur für C++, für die Template-Metaprogrammierung ist es nützlich, einige funktionale Programmiertechniken zu kennen. Heute schreibe ich nur zum Spaß eine Web-App in Python und OSC, weil ich etwas Neues lerne. –

+0

Wissen Sie, wie ich die funktionale Programmierung verbessern kann? Was würden Sie empfehlen? –

7

Ungeprüfte.

template < typename Tuple, typename T > 
struct tuple_push; 

template < typename T, typename ... Args > 
struct tuple_push<std::tuple<Args...>, T> 
{ 
    typedef std::tuple<Args...,T> type; 
}; 

template < typename Tuple > 
struct tuple_reverse; 

template < typename T, typename ... Args > 
struct tuple_reverse<std::tuple<T, Args...>> 
{ 
    typedef typename tuple_push<typename tuple_reverse<std::tuple<Args...>>::type, T>::type type; 
}; 

template < > 
struct tuple_reverse<std::tuple<>> 
{ 
    typedef std::tuple<> type; 
}; 

Irgendetwas gibt es sowieso.

Dies kehrt auch nur die Art, das, was Sie wollen zu sein scheint. Das Umkehren eines tatsächlichen Tupels würde Funktionen und keine Metafunktionen beinhalten.

1

Ich stieß auf diese Frage, während ich an den Umkehrschablonenparametern für beliebige Typen arbeitete.

Jonathan Wakely Antwort funktioniert gut für Tupel aber falls jemand anderes braucht jemals irgendeine Art zu umkehren, das heißt T<P1, P2, ..., Pn>-T<Pn, Pn-1, ..., P1>, hier ist das, was ich mit (Reversal logic taken from here) kam.

Einige der Implementierungslogik können kombiniert werden, aber ich habe versucht, es hier so klar wie möglich zu machen.

using my_tuple = std::tuple<int, bool, char>; 

static_assert(
    std::is_same< 
     typename reverse_type<my_typle>::type, 
     std::tuple<char, bool, int>>::value, 
    ""); 

oder andere Arten:

/// Standard collections cannot be directly reversed easily 
/// because they take default template parameters such as Allocator. 
template<typename K, typename V> 
struct simple_map : std::unordered_map<K, V> { }; 

static_assert(
    std::is_same< 
     typename reverse_type<simple_map<std::string, int>>::type, 
     simple_map<int, std::string>>::value, 
    ""); 

Slightly more detailed explanation

reverse_type können Tupel angewendet werden.

0

Interessanterweise wollten Sie wirklich einen Tupeltyp umkehren oder einfach jedes Element in umgekehrter Reihenfolge behandeln (wie es häufiger bei meinen Projekten der Fall ist)?

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

namespace detail { 

    template<class F, class Tuple, std::size_t...Is> 
    auto invoke_over_tuple(F &&f, Tuple &&tuple, std::index_sequence<Is...>) { 
     using expand = int[]; 
     void(expand{0, 
        ((f(std::get<Is>(std::forward<Tuple>(tuple)))), 0)...}); 
    } 


    template<class Sequence, std::size_t I> 
    struct append; 
    template<std::size_t I, std::size_t...Is> 
    struct append<std::index_sequence<Is...>, I> { 
     using result = std::index_sequence<Is..., I>; 
    }; 

    template<class Sequence> 
    struct reverse; 

    template<> 
    struct reverse<std::index_sequence<>> { 
     using type = std::index_sequence<>; 
    }; 

    template<std::size_t I, std::size_t...Is> 
    struct reverse<std::index_sequence<I, Is...>> { 
     using subset = typename reverse<std::index_sequence<Is...>>::type; 
     using type = typename append<subset, I>::result; 
    }; 
} 

template<class Sequence> 
using reverse = typename detail::reverse<Sequence>::type; 

template 
     < 
       class Tuple, 
       class F 
     > 
auto forward_over_tuple(F &&f, Tuple &&tuple) { 
    using tuple_type = std::decay_t<Tuple>; 
    constexpr auto size = std::tuple_size<tuple_type>::value; 
    return detail::invoke_over_tuple(std::forward<F>(f), 
            std::forward<Tuple>(tuple), 
            std::make_index_sequence<size>()); 
}; 

template 
     < 
       class Tuple, 
       class F 
     > 
auto reverse_over_tuple(F &&f, Tuple &&tuple) { 
    using tuple_type = std::decay_t<Tuple>; 
    constexpr auto size = std::tuple_size<tuple_type>::value; 
    return detail::invoke_over_tuple(std::forward<F>(f), 
            std::forward<Tuple>(tuple), 
            reverse<std::make_index_sequence<size>>()); 
}; 

int main() 
{ 
    auto t = std::make_tuple("1", 2, 3.3, 4.4, 5, 6, "7"); 
    forward_over_tuple([](auto &&x) { std::cout << x << " "; }, t); 
    std::cout << std::endl; 

    reverse_over_tuple([](auto &&x) { std::cout << x << " "; }, t); 
    std::cout << std::endl; 
} 
Verwandte Themen