這似乎是一個很簡單的問題:怎樣才能去掉第一(第n個)鍵入一個std::tuple
?卸下第一類型的std ::的元組
實施例:
typedef std::tuple<int, short, double> tuple1;
typedef std::tuple<short, double> tuple2;
如上所述將改變tuple1
成tuple2
操作。可能嗎?
這似乎是一個很簡單的問題:怎樣才能去掉第一(第n個)鍵入一個std::tuple
?卸下第一類型的std ::的元組
實施例:
typedef std::tuple<int, short, double> tuple1;
typedef std::tuple<short, double> tuple2;
如上所述將改變tuple1
成tuple2
操作。可能嗎?
可以使用一個簡單的類型的函數基於一類模板的部分特:
#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!"
);
}
啊,部分專精!謝謝,就是這樣! – cschwan 2013-02-13 11:40:20
+1,模板元編程是如此酷:) – StoryTeller 2013-02-13 11:40:51
@cschwan:不客氣:-)我添加了一個通用的版本,以從元組中刪除第i類型 – 2013-02-13 11:53:19
我想出了一個與@Andy提出的解決方案非常相似的解決方案,但它試圖通過直接在參數包(使用虛擬包裝)而不是在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
是一個輔助性的結構,其唯一目的是存儲模板參數包。然後可以使用unpack
將參數解壓縮到任意類模板(thanks to @BenVoigt for this trick)中。 prepend
只是將一種類型預先包裝。
remove_nth_type
使用部分模板特化從參數包中刪除第n個類型,將結果存儲到pack
。最後,remove_nth
接受一個任意類模板的一個特例,從它的模板參數去掉第n個類型,並返回新的專精。
我寫這被接受了入支持tuple_size
和tuple_element
API的C++ 14種標準使得它很容易對任何「元組樣」型的事,即一個proposal:
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)
測試與make_tuple(get<I>(t)...)
一個新的元組爲I
它:
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
我喜歡這個建議,並且已經發現我多次需要該功能。到目前爲止,我已經手工編寫了它,但標準實現將很好。希望它獲得批准。 – 2013-02-13 13:19:17
謝謝,我會在下次委員會會議等等細節有任何意見,將不勝感激地擁護它(我不確定如果通用模板是矯枉過正,只'需要int_seq',我想我會建議'apply()'應該被添加到)。我的電子郵件地址很容易從GCC郵件列表中找到。 – 2013-02-13 13:23:19
不錯的建議。不過,我可能會添加一個提示,即實際上可以在'O(log N)'複雜度中生成這樣一個列表。請參閱[這裏](http://stackoverflow.com/a/13073076/500104)。此外,編譯器編寫者可能想要使用他們的內置功能(IIRC,GCC和Clang都有類似'__builtin_make_indices')。 – Xeo 2013-02-13 13:30:50
這是template
元編程高於工程位此任務。它包括通過過濾器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
保存類型任意列表。請參閱@LucTouraille關於如何在tuple
和pack
之間移動的簡潔答案。
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
測試它是一種基於::value
濾波器去除0,filter_zero_out_b
這是一種基於::result
過濾器,也除去0
重載'操作者=',這需要'tuple'的這兩種類型的S' – 2013-02-13 11:34:45
實現的快速輪廓使得有能力*和時間*的人可以做出這樣的回答:)使一個沒有0的索引包,並且可變參數展開'get '到'make_tuple'上。 – 2013-02-13 11:34:59
@meh:這不會工作,因爲我對類型感興趣,而不是對象。 – cschwan 2013-02-13 11:37:43