2015-11-22 2 views
5

Der Einfachheit halber verwenden wir std::tuple als unsere Typenliste.Austauschen von zwei Typen in einer Typenliste

Was ist die beste (prägnante, am wenigsten rekursive, etc.) Möglichkeit, zwei Typen in einer std::tuple auszutauschen?

Illustration der Funktionalität durch die Verwendung von Indizes:

#include <tuple> 

int main() 
{ 
    using tuple_t = std::tuple<int, void, double>;   // int, void, double 
    using swapped_tuple_t = std::tuple<double, void, int>; // double, void, int 
    static_assert(std::is_same<swap<0, 2, tuple_t>::type, swapped_tuple_t>::value, "!"); 
} 

Antwort

6
#include <tuple> 
#include <utility> 
#include <cstddef> 

template <std::size_t I 
     , std::size_t J 
     , typename T 
     , typename = std::make_index_sequence<I> 
     , typename = std::make_index_sequence<J - I - 1> 
     , typename = std::make_index_sequence<std::tuple_size<T>::value - J - 1>> 
struct swap; 

template <std::size_t I 
     , std::size_t J 
     , typename T 
     , std::size_t... As 
     , std::size_t... Bs 
     , std::size_t... Cs> 
struct swap<I, J, T 
     , std::index_sequence<As...> 
     , std::index_sequence<Bs...> 
     , std::index_sequence<Cs...> 
     > 
{ 
    using type = std::tuple<typename std::tuple_element<As, T>::type... 
          , typename std::tuple_element<J, T>::type 
          , typename std::tuple_element<Bs + I + 1, T>::type... 
          , typename std::tuple_element<I, T>::type 
          , typename std::tuple_element<Cs + J + 1, T>::type...>; 
}; 

DEMO


Für Fälle, in denen J niedriger als oder gleich I sein kann, die unter Merkmal verwenden:

template <std::size_t I, std::size_t J, typename T> 
struct swap : swap_impl<I<J?I:J, I<J?J:I, T> {}; 

template <std::size_t I, typename T> 
struct swap<I,I,T> 
{ 
    using type = T; 
}; 

DEMO 2

+0

Wusste nicht über 'std :: make_index_sequence'. Schöne, saubere Lösung. – simpel01

+1

@MarcoA. Gut, um Probleme aufzuzeigen, aber es ist keine große Sache, da 'swap <2, 0>' nur in 'swap <0, 2>' umgewandelt werden kann. – user2296177

+0

@ user2296177 ja, der Code vor der Bearbeitung fehlgeschlagen für 'swap ' mit 'x> = y' .. trotzdem frage ich mich nur, wie kommt es nicht mit 'libC++' –

3

Ich werfe eine Alternative auf (aber ich denke, dass @ Piotrs Lösung eleganter zu sein).

template <size_t ...> 
struct seq { }; 

// X, Y are the indeces we want to swap 
template <size_t N, size_t X, size_t Y, size_t ...S> 
struct gen : gen<N-1, X, Y, (N-1 == X ? Y : (N-1 == Y ? X : N - 1)), S...> { }; 

template <size_t X, size_t Y, size_t ...S> 
struct gen<0, X, Y, S...> { 
    typedef seq<S...> type; 
}; 

// X and Y are the index we want to swap, T is the tuple 
template <size_t X, size_t Y, class T, class S> 
struct swapImpl; 

template <size_t X, size_t Y, class T, size_t... S> 
struct swapImpl<X, Y, T, seq<S...>>{ 
    using type = std::tuple<typename std::tuple_element<S, T>::type...>; 
}; 

template <size_t X, size_t Y, class T> 
struct swap { 
    using type = typename swapImpl<X, Y, T, 
         typename gen<std::tuple_size<T>::value, X, Y>::type>::type; 
}; 

int main() { 
    using tuple_t = std::tuple<int, unsigned, void, char, double>;   // int, void, double 
    using swapped_tuple_a_t = std::tuple<unsigned, int, void, char, double>; // double, void, int 
    static_assert(std::is_same<swap<0, 1, tuple_t>::type, swapped_tuple_a_t>::value, "!"); 
    static_assert(std::is_same<swap<1, 0, tuple_t>::type, swapped_tuple_a_t>::value, "!"); 
    using swapped_tuple_b_t = std::tuple<int, char, void, unsigned, double>; // double, void, int 
    static_assert(std::is_same<swap<1, 3, tuple_t>::type, swapped_tuple_b_t>::value, "!"); 
    static_assert(std::is_same<swap<3, 1, tuple_t>::type, swapped_tuple_b_t>::value, "!"); 
} 
4

Es gibt keinen Grund, drei Sequenzen zu verwenden. One ist genug:

template <std::size_t I 
     , std::size_t J 
     , typename T 
     , typename = std::make_index_sequence<std::tuple_size<T>::value>> 
struct swap_impl; 

template <std::size_t I 
     , std::size_t J 
     , typename T 
     , std::size_t... As> 
struct swap_impl<I, J, T 
       , std::index_sequence<As...> 
     > 
{ 
    using type = std::tuple<std::tuple_element_t<As == I ? J : As == J? I : As, T>... >; 
}; 

template <std::size_t I, std::size_t J, typename T> 
struct swap : swap_impl<I, J, T> {}; 

Und jetzt gibt es auch keine Notwendigkeit, Sonderfall der I> = J Fall.

Demo.

+0

Das ist eine schöne Technik, ich ' Ich werde es mir merken. Meine ursprüngliche Lösung war Piotrs ähnlich, aber ein wenig weniger elegant. Meiner Meinung nach ist dies klarer und deutlich prägnanter. – user2296177