2013-06-18 4 views
15

튜플의 형식을 어떻게 바꿀 수 있습니까? 예를 들어, reverse_tuple<std::tuple<int, char, bool>>::typestd::tuple<bool, char, int>으로 지정합니다. 다음과 같은 시도했지만 작동하지 않았다. 나는 무엇을 잘못 했는가?튜플 유형의 요소 유형 순서를 바꾸려면 어떻게해야합니까?

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> >’
: 여기

#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, "" 
    ); 
} 

내 오류입니다 prog.cpp: In function ‘int main()’:
prog.cpp:30:9: error: template argument 1 is invalid

+0

나는이 작업을 수행하기 위해 재귀를 필요가 있다고 생각하지 않습니다 [tuple_cat] (http://www.cplusplus.com/reference/tuple/tuple_cat/),하지만 당신은 튜플을 반전 할 왜 – aaronman

답변

15

는 여기에 있었다 : 안쪽에서 바깥에서 보면

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

, 당신은 튜플 요소의 순서 : 당신은 tuple에 결과를 넣어 tuple<Ts..., T>는, 당신은 그 반대로하려고, 그럼 당신은 ... 거꾸로하려고? 응?! :)

즉, tuple_reverse을 인스턴스화 할 때마다 동일한 크기의 튜플을 제공하므로 절대로 완료되지 않으며 반복적으로 영원히 인스턴스화됩니다. (그런 다음 재귀가 완료된 경우에도 결과 튜플 유형을 튜플에 넣으므로 N- 요소 튜플을 포함하는 단일 요소 튜플을 가지며 그 반대의 작업을 수행합니다. 단일 요소 튜플을 반전하는 것이 무 조작 없음)

당신은 요소 중 하나를 벗겨하려면, 다음 나머지를 역으로 다시 다시 연결할 :.

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>())); 

을 그리고 당신은 튜플에 포장 할 필요가 없습니다 및 다시 역으로 :)

빈 터플 케이스도 처리해야하므로 모든 것이 다음과 같습니다.

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>())); 
}; 

나는 다르게 할 것입니다.

는 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>> 
{ }; 

를 사용하여, 단지 유형을 얻으려면 또는 당신은, 실제 튜플 객체를 역 유형을 얻을 수 decltype(reverse(t))를 사용하는 함수를 작성할 수 있습니다.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>()); 
} 

C++ (11) 사용 <integer_seq.h>에서와 반환 형식을 추가하고 tuple_sizetuple_element이 작동하지 않기 때문에 (튜플 형식에서 참조를 제거하는 remove_reference를 사용 튜플에 대한 참조와 함께) :

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

나는 이걸로 어떻게 너만큼 잘해? 나는이 모든 놀라운 방법으로 그것을 쓰려고 생각하지 않았을 것입니다! :) –

+0

연습, 연습, 연습. C++뿐만 아니라, 템플릿 메타 프로그래밍의 경우 일부 프로그래밍 프로그래밍 기술을 아는 것이 유용합니다. 오늘 저는 Python과 OSC로 재미있는 웹 애플리케이션을 작성하고 있습니다. 새로운 것을 배우고 있기 때문입니다. –

+0

함수 프로그래밍을 어떻게 향상시킬 수 있는지 알고 계십니까? 무엇을 추천하나요? –

7

테스트되지 않은.

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; 
}; 

어쨌든 거기에 뭔가가 있습니다.

이것은 또한 유형을 뒤집습니다. 실제 튜플을 반대로 변환하면 메타 함수가 아닌 함수가 사용됩니다. 당신이 뭘 잘못했는지

1

임의 유형에 대한 역방향 템플릿 매개 변수를 작업하는 동안이 질문을 보게되었습니다.

조나단 와클리의 대답은 튜플에 매우 유용합니다. 다른 유형 (예 : T<P1, P2, ..., Pn> ~ T<Pn, Pn-1, ..., P1>)을 되돌릴 필요가있는 경우 여기 (Reversal logic taken from here)가 있습니다.

namespace Details 
{ 
    /// Get the base case template type `T<>` of a templated type `T<...>` 
    template<typename> 
    struct templated_base_case; 

    template <template<typename...> class T, typename... TArgs> 
    struct templated_base_case<T<TArgs...>> 
    { 
     using type = T<>; 
    }; 

    /// Inner reverse logic. 
    /// 
    /// Reverses the template parameters of a templated type `T` such 
    /// that `T<A, B, C>` becomes `T<C, B, A>`. 
    /// 
    /// Note that this requires `T<>` to exist. 
    template< 
     typename T, 
     typename = typename templated_base_case<T>::type> 
    struct reverse_impl; 

    template< 
     template <typename...> class T, 
     typename... TArgs> 
    struct reverse_impl< 
     typename templated_base_case<T<TArgs...>>::type, 
     T<TArgs...>> 
    { 
     using type = T<TArgs...>; 
    }; 

    template< 
     template<typename...> class T, 
     typename first, 
     typename... rest, 
     typename... done> 
    struct reverse_impl< 
     T<first, rest...>, 
     T<done...>> 
    { 
     using type = typename reverse_impl <T<rest...>, T<first, done...>>::type; 
    }; 

    /// Swap template parameters of two templated types. 
    /// 
    /// `L<A, B, C> and R<X, Y, Z>` become `L<X, Y, Z> and R<A, B, C>`. 
    template<typename L, typename R> 
    struct swap_template_parameters; 

    template< 
     template<typename...> class L, 
     template<typename...> class R, 
     typename... x, 
     typename... y> 
    struct swap_template_parameters<L<x...>, R<y...>> 
    { 
     using left_type = L<y...>; 
     using right_type = R<x...>; 
    }; 
} 

/// Parameter pack list of types 
template <typename... Args> 
struct type_list { }; 

/// Reverses the arguments of a templates type `T`. 
/// 
/// This uses a `type_list` to allow reversing types like std::pair 
/// where `std::pair<>` and `std::pair<T>` are not valid. 
template<typename T> 
struct reverse_type; 

template<template<typename...> class T, typename... TArgs> 
struct reverse_type<T<TArgs...>> 
{ 
    using type = typename Details::swap_template_parameters< 
     T<TArgs...>, 
     typename Details::reverse_impl<type_list<TArgs...>>::type>::left_type; 
}; 

일부 구현 로직을 결합 할 수 있지만 가능한 한 명확하게하려고했습니다.

reverse_type는 튜플에 적용 할 수 있습니다

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, 
    ""); 

또는 다른 유형 :

/// 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합니다.

0

관심의 대상에서 정말로 튜플 유형을 바꾸거나 각 요소를 역순으로 처리하고 싶습니까 (더 자주 프로젝트에서 그렇듯이)?

#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; 
} 
관련 문제