这似乎是一个很简单的问题:如何删除std::tuple中的第一个(第n个)类型?示例:
std::tuple
typedef std::tuple<int, short, double> tuple1; typedef std::tuple<short, double> tuple2;
上面描述的操作会将tuple1转换为tuple2,这可能吗?
tuple1
tuple2
rsl1atfo1#
可以使用基于类模板的部分专用化的简单类型函数:
#include <type_traits> #include <tuple> using namespace std; template<typename T> struct remove_first_type { }; template<typename T, typename... Ts> struct remove_first_type<tuple<T, Ts...>> { typedef tuple<Ts...> type; }; int main() { typedef tuple<int, bool, double> my_tuple; typedef remove_first_type<my_tuple>::type my_tuple_wo_first_type; static_assert( is_same<my_tuple_wo_first_type, tuple<bool, double>>::value, "Error!" ); }
而且,该解决方案可以容易地推广为移除元组的第i个类型:
#include <type_traits> #include <tuple> using namespace std; template<size_t I, typename T> struct remove_ith_type { }; template<typename T, typename... Ts> struct remove_ith_type<0, tuple<T, Ts...>> { typedef tuple<Ts...> type; }; template<size_t I, typename T, typename... Ts> struct remove_ith_type<I, tuple<T, Ts...>> { typedef decltype( tuple_cat( declval<tuple<T>>(), declval<typename remove_ith_type<I - 1, tuple<Ts...>>::type>() ) ) type; }; int main() { typedef tuple<int, bool, double> my_tuple; typedef remove_ith_type<1, my_tuple>::type my_tuple_wo_2nd_type; static_assert( is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value, "Error!" ); }
tcomlyy62#
我写了一个proposal,它被C++14标准接受,这使得它很容易为任何“元组类”类型做,即支持tuple_size和tuple_element API的类型:
tuple_size
tuple_element
template<typename T, typename Seq> struct tuple_cdr_impl; template<typename T, std::size_t I0, std::size_t... I> struct tuple_cdr_impl<T, std::index_sequence<I0, I...>> { using type = std::tuple<typename std::tuple_element<I, T>::type...>; }; template<typename T> struct tuple_cdr : tuple_cdr_impl<T, std::make_index_sequence<std::tuple_size<T>::value>> { };
而且,只需几个函数就可以将元组对象转换为新类型:
template<typename T, std::size_t I0, std::size_t... I> typename tuple_cdr<typename std::remove_reference<T>::type>::type cdr_impl(T&& t, std::index_sequence<I0, I...>) { return std::make_tuple(std::get<I>(t)...); } template<typename T> typename tuple_cdr<typename std::remove_reference<T>::type>::type cdr(T&& t) { return cdr_impl(std::forward<T>(t), std::make_index_sequence<std::tuple_size<T>::value>{}); }
这将创建整数序列[0,1,2,...,N),其中N是tuple_size<T>::value,然后在[1,2,...,N)中为I创建具有make_tuple(get<I>(t)...)的新元组测试:
[0,1,2,...,N)
N
tuple_size<T>::value
[1,2,...,N)
I
make_tuple(get<I>(t)...)
using tuple1 = std::tuple<int, short, double>; using tuple2 = std::tuple<short, double>; using transformed = decltype(cdr(std::declval<tuple1>())); static_assert(std::is_same<transformed, tuple2>::value, ""); static_assert(std::is_same<tuple_cdr<tuple1>::type, tuple2>::value, ""); #include <iostream> int main() { auto t = cdr(std::make_tuple(nullptr, "hello", "world")); std::cout << std::get<0>(t) << ", " << std::get<1>(t) << '\n'; }
我的建议书参考实现位于https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h
yvgpqqbh3#
我提出了一个与@Andy提出的解决方案非常相似的解决方案,但是它试图通过直接处理参数包(使用一个伪 Package 器)而不是std::tuple来更通用一些,这样,该操作也可以应用于其他可变参数模板,而不仅仅是元组:
#include <type_traits> #include <tuple> template <typename... Args> struct pack {}; template <template <typename...> class T, typename Pack> struct unpack; template <template <typename...> class T, typename... Args> struct unpack<T, pack<Args...>> { typedef T<Args...> type; }; template <typename T, typename Pack> struct prepend; template <typename T, typename... Args> struct prepend<T, pack<Args...>> { typedef pack<T, Args...> type; }; template <std::size_t N, typename... Args> struct remove_nth_type; template <std::size_t N, typename T, typename... Ts> struct remove_nth_type<N, T, Ts...> : prepend<T, typename remove_nth_type<N-1, Ts...>::type> {}; template <typename T, typename... Ts> struct remove_nth_type<0, T, Ts...> { typedef pack<Ts...> type; }; template <typename T, int N> struct remove_nth; template <template <typename...> class T, int N, typename... Args> struct remove_nth<T<Args...>, N> { typedef typename unpack< T, typename remove_nth_type<N, Args...>::type >::type type; }; template <typename... Args> struct my_variadic_template { }; int main() { typedef std::tuple<int, bool, double> my_tuple; typedef remove_nth<my_tuple, 1>::type my_tuple_wo_2nd_type; static_assert( is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value, "Error!" ); typedef my_variadic_template<int, double> vt; typedef remove_nth<vt, 0>::type vt_wo_1st_type; static_assert( is_same<vt_wo_1st_type, my_variadic_template<double>>::value, "Error!" ); }
pack是一个helper结构,其唯一目的是存储模板参数包。unpack然后可用于将参数解包到任意类模板(thanks to @BenVoigt for this trick)中。prepend只是将类型添加到包的前面。remove_nth_type使用部分模板专门化从参数包中移除第n个类型,将结果存储到pack中。最后,remove_nth获取任意类模板的专门化,从其模板参数中移除第n个类型,并返回新的专门化。
pack
unpack
prepend
remove_nth_type
remove_nth
2w2cym1i4#
除了那些疯狂的TMP东西,还有一种非常简单的方法来使用C++17 STL函数std::apply:
std::apply
#include <string> #include <tuple> template <class T, class... Args> auto tail(const std::tuple<T, Args...>& t) { return std::apply( [](const T&, const Args&... args) { return std::make_tuple(args...); }, t); } template <class T> using tail_t = decltype(tail(T{})); int main() { std::tuple<int, double, std::string> t{1, 2., "3"}; auto _2_3 = tail(t); using tuple_t = tail_t<std::tuple<int, double, std::string>>; static_assert(std::is_same_v<std::tuple<double, std::string>, tuple_t>); }
DEMO.
dgjrabp25#
这是template元编程的一个过度设计的部分,它包括通过过滤器template对tuple的类型进行任意重排序/复制/删除的能力:
template
tuple
#include <utility> #include <type_traits> template<typename... Ts> struct pack {}; template<std::size_t index, typename Pack, typename=void> struct nth_type; template<typename T0, typename... Ts> struct nth_type<0, pack<T0, Ts...>, void> { typedef T0 type; }; template<std::size_t index, typename T0, typename... Ts> struct nth_type<index, pack<T0, Ts...>, typename std::enable_if<(index>0)>::type>: nth_type<index-1, pack<Ts...>> {}; template<std::size_t... s> struct seq {}; template<std::size_t n, std::size_t... s> struct make_seq:make_seq<n-1, n-1, s...> {}; template<std::size_t... s> struct make_seq<0,s...> { typedef seq<s...> type; }; template<typename T, typename Pack> struct conc_pack { typedef pack<T> type; }; template<typename T, typename... Ts> struct conc_pack<T, pack<Ts...>> { typedef pack<T, Ts...> type; }; template<std::size_t n, typename Seq> struct append; template<std::size_t n, std::size_t... s> struct append<n, seq<s...>> { typedef seq<n, s...> type; }; template<typename S0, typename S1> struct conc; template<std::size_t... s0, std::size_t... s1> struct conc<seq<s0...>, seq<s1...>> { typedef seq<s0..., s1...> type; }; template<typename T, typename=void> struct value_exists:std::false_type {}; template<typename T> struct value_exists<T, typename std::enable_if< std::is_same<decltype(T::value),decltype(T::value)>::value >::type >:std::true_type {}; template<typename T, typename=void> struct result_exists:std::false_type {}; template<typename T> struct result_exists<T, typename std::enable_if< std::is_same<typename T::result,typename T::result>::value >::type >:std::true_type {}; template<template<std::size_t>class filter, typename Seq, typename=void> struct filter_seq { typedef seq<> type; }; template<template<std::size_t>class filter, std::size_t s0, std::size_t... s> struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<value_exists<filter<s0>>::value>::type> : append< filter<s0>::value, typename filter_seq<filter, seq<s...>>::type > {}; template<template<std::size_t>class filter, std::size_t s0, std::size_t... s> struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && result_exists<filter<s0>>::value>::type> : conc< typename filter<s0>::result, typename filter_seq<filter, seq<s...>>::type > {}; template<template<std::size_t>class filter, std::size_t s0, std::size_t... s> struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && !result_exists<filter<s0>>::value>::type> : filter_seq<filter, seq<s...>> {}; template<typename Seq, typename Pack> struct remap_pack { typedef pack<> type; }; template<std::size_t s0, std::size_t... s, typename Pack> struct remap_pack< seq<s0, s...>, Pack > { typedef typename conc_pack< typename nth_type<s0, Pack>::type, typename remap_pack< seq<s...>, Pack >::type >::type type; }; template<typename Pack> struct get_indexes { typedef seq<> type; }; template<typename... Ts> struct get_indexes<pack<Ts...>> { typedef typename make_seq< sizeof...(Ts) >::type type; }; template<std::size_t n> struct filter_zero_out { enum{ value = n }; }; template<> struct filter_zero_out<0> {}; template<std::size_t n> struct filter_zero_out_b { typedef seq<n> result; }; template<> struct filter_zero_out_b<0> { typedef seq<> result; }; #include <iostream> int main() { typedef pack< int, double, char > pack1; typedef pack< double, char > pack2; typedef filter_seq< filter_zero_out, typename get_indexes<pack1>::type >::type reindex; typedef filter_seq< filter_zero_out_b, typename get_indexes<pack1>::type >::type reindex_b; typedef typename remap_pack< reindex, pack1 >::type pack2_clone; typedef typename remap_pack< reindex_b, pack1 >::type pack2_clone_b; std::cout << std::is_same< pack2, pack2_clone >::value << "\n"; std::cout << std::is_same< pack2, pack2_clone_b >::value << "\n"; }
这里我们有一个类型pack,它包含任意类型列表,关于如何在tuple和pack之间移动,请参见@LucTouraille的简洁回答。seq保存索引序列。remap_pack获取seq和pack,并通过获取原始pack的第n个元素来构建结果pack。filter_seq采用template<size_t>函子和seq,并使用该函子筛选seq的元素。该函子可以返回size_t类型的::value或seq<...>类型的::result,也可以两者都不返回,允许一对一或一对多函子。其他一些辅助函数,如conc、append、conc_pack、get_indexes、make_seq、nth_type,可以使这些功能更加完善。我用filter_zero_out和filter_zero_out_b进行了测试,filter_zero_out是一个基于::value的过滤器,可以删除0,filter_zero_out_b是一个基于::result的过滤器,也可以删除0。
seq
remap_pack
filter_seq
template<size_t>
size_t
::value
seq<...>
::result
conc
append
conc_pack
get_indexes
make_seq
nth_type
filter_zero_out
filter_zero_out_b
5条答案
按热度按时间rsl1atfo1#
可以使用基于类模板的部分专用化的简单类型函数:
而且,该解决方案可以容易地推广为移除元组的第i个类型:
tcomlyy62#
我写了一个proposal,它被C++14标准接受,这使得它很容易为任何“元组类”类型做,即支持
tuple_size
和tuple_element
API的类型:而且,只需几个函数就可以将元组对象转换为新类型:
这将创建整数序列
[0,1,2,...,N)
,其中N
是tuple_size<T>::value
,然后在[1,2,...,N)
中为I
创建具有make_tuple(get<I>(t)...)
的新元组测试:
我的建议书参考实现位于https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h
yvgpqqbh3#
我提出了一个与@Andy提出的解决方案非常相似的解决方案,但是它试图通过直接处理参数包(使用一个伪 Package 器)而不是
std::tuple
来更通用一些,这样,该操作也可以应用于其他可变参数模板,而不仅仅是元组:pack
是一个helper结构,其唯一目的是存储模板参数包。unpack
然后可用于将参数解包到任意类模板(thanks to @BenVoigt for this trick)中。prepend
只是将类型添加到包的前面。remove_nth_type
使用部分模板专门化从参数包中移除第n个类型,将结果存储到pack
中。最后,remove_nth
获取任意类模板的专门化,从其模板参数中移除第n个类型,并返回新的专门化。2w2cym1i4#
除了那些疯狂的TMP东西,还有一种非常简单的方法来使用C++17 STL函数
std::apply
:DEMO.
dgjrabp25#
这是
template
元编程的一个过度设计的部分,它包括通过过滤器template
对tuple
的类型进行任意重排序/复制/删除的能力:这里我们有一个类型
pack
,它包含任意类型列表,关于如何在tuple
和pack
之间移动,请参见@LucTouraille的简洁回答。seq
保存索引序列。remap_pack
获取seq
和pack
,并通过获取原始pack
的第n个元素来构建结果pack
。filter_seq
采用template<size_t>
函子和seq
,并使用该函子筛选seq
的元素。该函子可以返回size_t
类型的::value
或seq<...>
类型的::result
,也可以两者都不返回,允许一对一或一对多函子。其他一些辅助函数,如
conc
、append
、conc_pack
、get_indexes
、make_seq
、nth_type
,可以使这些功能更加完善。我用
filter_zero_out
和filter_zero_out_b
进行了测试,filter_zero_out
是一个基于::value
的过滤器,可以删除0,filter_zero_out_b
是一个基于::result
的过滤器,也可以删除0。