【问题标题】:How do I reverse the order of element types in a tuple type?如何反转元组类型中元素类型的顺序?
【发布时间】:2013-06-15 05:03:10
【问题描述】:

如何反转元组中的类型?例如,我希望 reverse_tuple<std::tuple<int, char, bool>>::type 成为 std::tuple<bool, char, int>。我尝试执行以下操作,但没有成功。我做错了什么?

#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 instantiation of ‘struct tuple_reverse&lt;std::tuple&lt;char, int, bool&gt; &gt;’:
prog.cpp:15:34: recursively required from ‘struct tuple_reverse&lt;std::tuple&lt;bool, char, int&gt; &gt;’
prog.cpp:15:34: required from ‘struct tuple_reverse&lt;std::tuple&lt;int, bool, char&gt; &gt;’
prog.cpp:29:31: required from here
prog.cpp:15:34: error: no type named ‘type’ in ‘struct tuple_reverse&lt;std::tuple&lt;int, bool, char&gt; &gt;’
prog.cpp: In function ‘int main()’:
prog.cpp:30:9: error: template argument 1 is invalid

【问题讨论】:

  • 我认为你不需要递归来做到这一点,tuple_cat,但你为什么要反转一个元组

标签: c++ c++11 tuples variadic-templates


【解决方案1】:

未经测试。

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

总之有些事情。

这也只会反转类型,这似乎是您所追求的。反转一个实际的元组将涉及函数,而不是元函数。

【讨论】:

    【解决方案2】:

    你做错了什么:

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

    从内到外看,你对元组元素重新排序:tuple&lt;Ts..., T&gt;,然后尝试反转它,然后将结果放入 tuple,然后尝试反转 那个 em> ...嗯?! :)

    这意味着每次实例化 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 中使用 &lt;integer_seq.h&gt; 并添加返回类型并使用 remove_reference 从元组类型中去除引用(因为 tuple_sizetuple_element 不适用于对元组的引用):

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

    【讨论】:

    • 我怎样才能变得和你一样优秀?我从来没有想过用所有这些惊人的方式来写它! :)
    • 练习,练习,练习。不仅仅是 C++,对于模板元编程,了解一些函数式编程技术很有用。今天我用 Python 和 OSC 编写一个网络应用程序只是为了好玩,因为我正在学习一些新东西。
    • 你知道我可以如何改进函数式编程吗?你会推荐什么?
    • @Jonathan :我正在尝试编译您的代码,但我不断收到以下错误 error C2893: Failed to specialize function template 'unknown-type reverse(T &&)' 1> 使用以下模板参数: 1> 'T=std::tuple' 1> 'TT=std::tuple' 有什么想法吗?
    • 请注意。如果在扩展可变参数时在表达式中使用索引变量两次,英特尔编译器(至少 icc 16)会失败。
    【解决方案3】:

    我在处理任意类型的反转模板参数时遇到了这个问题。

    Jonathan Wakely 的回答非常适合元组,但如果其他人需要反转任何类型,即 T&lt;P1, P2, ..., Pn&gt;T&lt;Pn, Pn-1, ..., P1&gt;,这就是我想出的 (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.

    【讨论】:

      【解决方案4】:

      出于兴趣,您真的想反转元组类型,还是只是以相反的顺序处理每个元素(在我的项目中经常出现这种情况)?

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-24
        • 1970-01-01
        • 1970-01-01
        • 2020-06-04
        • 2016-04-28
        • 1970-01-01
        相关资源
        最近更新 更多