如何反转元组类型中元素类型的顺序
How do I reverse the order of element types in a tuple type?
如何反转元组中的类型?例如,我想让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<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
你做错的地方在这里:
using type = typename tuple_reverse<
std::tuple<
typename tuple_reverse<std::tuple<Ts..., T>>::type
>
>::type;
从内到外看,你重新排序元组元素:tuple<Ts..., T>
,然后你试着反转,然后你把结果放在tuple
,然后你试着反转…啊? !:)
这意味着每次实例化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>
并添加返回类型,并使用remove_reference
从元组类型中删除引用(因为tuple_size
和tuple_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>());
}
未经测试
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;
};
无论如何都有一些东西。
这也只是反转类型,这似乎是你想要的。反转一个实际的元组需要用到函数,而不是元函数。
我在为任意类型反转模板参数时遇到了这个问题。
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;
};
一些实现逻辑可以组合在一起,但我在这里尽量使它尽可能清晰。
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,
"");
更详细的说明
我结合了Faheem Mitha的SequenceHelper和Jonathan Wakely的tuple_reverse来反转元组类型和数据。此解决方案适用于c++11。
#include <tuple>
#include <algorithm>
#include <vector>
namespace TupleHelper {
namespace SequenceHelper {
// Tuple to parameter pack
// Original Author: Faheem Mitha https://stackoverflow.com/users/350713/faheem-mitha
// https://stackoverflow.com/questions/36612596/tuple-to-parameter-pack
//
// License: Creative Commons Attribution-ShareAlike (CC-BY-SA)
// https://www.ictrecht.nl/en/blog/what-is-the-license-status-of-stackoverflow-code-snippets
template<int ...>
struct seq { };
template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> {};
template<int ...S>
struct gens<0, S...> {
typedef seq<S...> type;
#if __GNUC__
virtual ~gens() {} // this is only to avoid -Weffc++ warning
#endif
};
// end of Faheem Mitha's code
} // namespace SequenceHelper
// reverses the tuple types
// How do I reverse the order of element types in a tuple type?
// Original Author: Jonathan Wakely https://stackoverflow.com/users/981959/jonathan-wakely
// https://stackoverflow.com/a/17178399
//
// License: Creative Commons Attribution-ShareAlike (CC-BY-SA)
// https://www.ictrecht.nl/en/blog/what-is-the-license-status-of-stackoverflow-code-snippets
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>()));
};
// end of Jonathan Wakely's code
// reverses the content of the tuple too
namespace TupleReverseImpl {
// Helper functions to recursivly copy the source tuple to the target tuple
template <class ...Args>
void doCopyTypes( std::vector<void*>::size_type index,
std::vector<const void*> & content_source,
std::vector<void*> & content_target ) {}
template <class T, class ...Args>
void doCopyTypes( std::vector<void*>::size_type index,
std::vector<const void*> & content_source,
std::vector<void*> & content_target,
const T * t,
Args... args )
{
// target and source vector do have the types in the same order
// so we can cast here to the correct type, then copy it
T * ptrSource = reinterpret_cast<T*>(const_cast<void*>(content_source.at(index)));
T * ptrTarget = reinterpret_cast<T*>(content_target.at(index));
*ptrTarget = *ptrSource;
doCopyTypes( index+1, content_source, content_target, args... );
}
template <class Tuple, int ...S>
void copyTypes( std::vector<const void*> & content_source,
std::vector<void*> & content_target,
Tuple & tuple,
SequenceHelper::seq<S...> )
{
doCopyTypes( 0, content_source, content_target, &std::get<S>(tuple)... );
}
// Helper functions to fill a vector of pointers, to prepare copying
template <class V>
void dofillContent( V & content ) {}
template <class V, class T, class ...Args>
void dofillContent( V & content, T * t, Args... args )
{
content.push_back( t );
dofillContent( content, args... );
}
template <class V,class Tuple, int ...S>
void fill( V & content, Tuple & tuple, SequenceHelper::seq<S...> )
{
dofillContent( content, &std::get<S>(tuple)... );
}
} // namespace TupleReverseImpl
/*
* this reverses a tuple and its content
*
* std::tuple<int,bool,std::string> reverse = TupleHelper::reverseTuple( std::make_tuple( std::string( "one", true, 42 ) ) );
*/
template <class Tuple>
typename tuple_reverse<Tuple>::type reverseTuple( const Tuple & tuple )
{
// declare return type
typedef typename tuple_reverse<Tuple>::type REVERSE_TUPLE_TYPE;
REVERSE_TUPLE_TYPE return_tuple;
// create source and target pointer vectors for the copy action
std::vector<const void*> contentSource;
std::vector<void*> contentTarget;
TupleReverseImpl::fill( contentSource, tuple, typename SequenceHelper::gens<std::tuple_size<Tuple>::value>::type() );
TupleReverseImpl::fill( contentTarget, return_tuple, typename SequenceHelper::gens<std::tuple_size<REVERSE_TUPLE_TYPE>::value>::type() );
// to be in the same order as contentTarget
std::reverse(contentTarget.begin(), contentTarget.end() );
// now copy everything
TupleReverseImpl::copyTypes( contentSource, contentTarget, tuple, typename SequenceHelper::gens<std::tuple_size<Tuple>::value>::type() );
return return_tuple;
}
} // namespace TupleHelper
int main()
{
std::tuple<int,bool,std::string> reverse = TupleHelper::reverseTuple( std::make_tuple( std::string( "one", true, 42 ) ) );
}
出于兴趣,您是否真的想要反转元组类型,或者只是以反向顺序处理每个元素(就像我的项目中更常见的情况一样)?
#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;
}
#include <tuple>
template<typename T>
struct Reverse;
template<typename... Ts>
struct Reverse<std::tuple<Ts...>>
{
using type = std::tuple<typename std::tuple_element<sizeof...(Ts) - 1 - i, std::tuple<Ts...>>::type...>;
};
// usage example
static_assert(std::is_same_v<typename Reverse<std::tuple<int, bool, double>>::type, std::tuple<double, bool, int>>);
using this method you can reverse the tuple type.
- C++中数据类型修饰符的顺序
- 如何检查参数包是否具有执行顺序中的确切类型
- 如果在 C++ 构造函数中以错误的顺序初始化对象数据,会发生什么类型的错误
- 类型名 C++ 和编译顺序
- 如何实现对参数顺序不可知的std::same_as的广义形式(即对于两个以上的类型参数)
- 为什么重载的函数模板顺序对基本类型很重要
- 交换未定义数据类型中的字节顺序
- 验证(使用 static_assert)元组类型是否遵循某种顺序(有状态编译时检查)
- C/C++ - 用于按顺序打印数字的 sem_t 类型的单个信号量
- 初始化与类类型相同的静态成员(静态初始化顺序问题)
- 使用枚举类型管理存储顺序
- C++ MDC final-在字符类型的数组结构中按字母顺序对记录中的名称进行排序
- C++11 标准中的哪一部分规定了基元数据类型大小之间的相对顺序?
- 检查两个 Boost.MPL 序列是否按任何顺序包含相同的类型
- 类型(x0 = y0,x1 = y1,x2 = x0,..)的括号和逗号表达中的评估顺序
- 正确的方法或设计模式,以简化类中的“operatorX”函数,以按给定顺序比较相同类型的属性
- 顺序自定义类型特征的行为就像一个
- Boost MPL-按顺序声明每种类型的类
- 为什么指针类型的内置关系运算符不生成C++的总顺序?
- 如何在不考虑类型顺序的情况下比较相同类型的元组