简体   繁体   English

如何反转元组类型中元素类型的顺序?

[英]How do I reverse the order of element types in a tuple type?

How do I reverse the types in a tuple? 如何反转元组中的类型? For example, I want reverse_tuple<std::tuple<int, char, bool>>::type to be std::tuple<bool, char, int> . 例如,我想将reverse_tuple<std::tuple<int, char, bool>>::type作为std::tuple<bool, char, int> I tried doing the following but it didn't work. 我尝试了以下但是没有用。 What did I do wrong? 我做错了什么?

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

Here are my errors: 这是我的错误:

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

What you did wrong was here: 你做错了什么在这里:

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

Looking at it from the inside out, you reorder the tuple elements: tuple<Ts..., T> , then you try to reverse that, then you put the result in a tuple , then you try to reverse that ... huh?! 从内到外看,你重新排序元组元素: tuple<Ts..., T> ,然后你试图反转它,然后你把结果放在一个tuple ,然后你试图扭转那个 ......呵呵?! :) :)

This means each time you instantiate tuple_reverse you give it a tuple of the same size, so it never finishes, and recursively instantiates itself forever. 这意味着每次实例化tuple_reverse都会给它一个相同大小的元组,因此它永远不会完成,并且会永久地递归实例化它自己。 (Then, if that recursion even finished, you put the resulting tuple type into a tuple, so you have a single-element tuple containing an N-element tuple, and reverse that, which does nothing because reversing a single-element tuple is a no-op.) (然后,如果该递归甚至完成,你将结果元组类型放入一个元组中,所以你有一个包含N元素元组的单元素元组,并反转它,这没有任何作用,因为反转单元素元组是一个无操作)。

You want to peel off one of the elements, then reverse the rest, and concatenate it back again: 你想剥离其中一个元素,然后反转剩下的元素,然后再将它连接起来:

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

And you don't need to wrap it in a tuple and reverse it again :) 而且你不需要将它包装在一个元组中并再次反转:)

And you should also handle the empty tuple case, so the whole thing is: 你还应该处理空元组的情况,所以整个事情是:

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

I'd do it differently though. 我会做不同的事情。

To get just the type, using C++14 为了获得类型,使用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>>
{ };

Or you can write a function to reverse an actual tuple object, then use decltype(reverse(t)) to get the type. 或者您可以编写一个函数来反转实际的元组对象,然后使用decltype(reverse(t))来获取类型。 To reverse a tuple-like object in C++14: 要在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 use <integer_seq.h> and add return types and use remove_reference to strip references from the tuple type (because tuple_size and tuple_element don't work with references to tuples): 在C ++ 11中使用<integer_seq.h>并添加返回类型并使用remove_reference从元组类型中tuple_size引用(因为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>());
}

Untested. 未经测试。

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

Something there abouts anyway. 无论如何,那里有些东西。

This also only reverses the type, which seems to be what you're after. 这也只是颠倒了类型,这似乎是你所追求的。 Reversing an actual tuple would involve functions, not metafunctions. 反转实际元组将涉及函数,而不是元函数。

I came across this question while working on the reversing template parameters for arbitrary types. 我在处理任意类型的反转模板参数时遇到了这个问题。

Jonathan Wakely's answer works great for tuples but in case anyone else ever needs to reverse any type, ie T<P1, P2, ..., Pn> to T<Pn, Pn-1, ..., P1> , here is what I came up with ( Reversal logic taken from here ). Jonathan Wakely的答案适用于元组,但万一其他人需要反转任何类型,即T<P1, P2, ..., Pn>T<Pn, Pn-1, ..., P1> ,这里是我想出了什么( 从这里采取逆转逻辑 )。

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

Some of the implementation logic can be combined, but I tried to make it as clear as possible here. 一些实现逻辑可以组合,但我试图在这里尽可能清楚。

reverse_type can be applied to tuples: 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,
    "");

Or other types: 或其他类型:

/// 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 . 稍微详细解释一下

Out of interest, did you really want to reverse a tuple type, or just treat each element in reverse order (as is more often the case in my projects)? 出于兴趣,您是否真的想要反转元组类型,或者只是以相反的顺序处理每个元素(在我的项目中更常见)?

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM