まず、インデックス配列の概要:
template<std::size_t ...S>
struct seq { };
// And now an example of how index arrays are used to print a tuple:
template <typename ...T, std::size_t ...S>
void print_helper(std::tuple<T...> tup, seq<S...> s) {
// this trick is exceptionally useful:
// ((std::cout << std::get<S>(tup) << " "), 0) executes the cout
// and returns 0.
// { 0... } expands (because the expression has an S in it),
// returning an array of length sizeof...(S) full of zeros.
// The array isn't used, but it's a great hack to do one operation
// for each std::size_t in S.
int garbage[] = { ((std::cout << std::get<S>(tup) << " "), 0)... };
std::cout << std::endl;
}
次に、print_helper関数を使用します。
int main() {
print_helper(std::make_tuple(10, 0.66, 'h'), seq<0,1,2>() );
return 0;
}
ただし、入力seq<0,1,2>
するのは少し面倒です。したがって、テンプレート再帰を使用して、を生成するクラスを作成できますseq
。これgens<3>::type
は、次と同じseq<0,1,2>
です。
template<std::size_t N, std::size_t ...S>
struct gens : gens<N-1, N-1, S...> { };
template<std::size_t ...S>
struct gens<0, S...> {
typedef seq<S...> type;
};
int main() {
print_helper(std::make_tuple(10, 0.66, 'h'), gens<3>::type() );
return 0;
}
N
ingens<N>::type
は常にタプル内の要素の数になるため、ラップして簡単にすることができますprint_helper
。
template <typename ...T>
void print(std::tuple<T...> tup) {
print_helper(tup, typename gens<sizeof...(T)>::type() );
}
int main() {
print(std::make_tuple(10, 0.66, 'h'));
return 0;
}
テンプレートの引数は自動的に推測できることに注意してください(すべてを入力するのは面倒ですよね?)。
さて、tuple_zip
関数:
前と同じように、ヘルパー関数から始めます。
template <template <typename ...> class Tup1,
template <typename ...> class Tup2,
typename ...A, typename ...B,
std::size_t ...S>
auto tuple_zip_helper(Tup1<A...> t1, Tup2<B...> t2, seq<S...> s) ->
decltype(std::make_tuple(std::make_pair(std::get<S>(t1),std::get<S>(t2))...)) {
return std::make_tuple( std::make_pair( std::get<S>(t1), std::get<S>(t2) )...);
}
コードは少し注意が必要です。特に、末尾の戻り型(戻り型は、パラメーターが定義された後に宣言されauto
、提供されます)。これにより、関数本体で使用される式を返すことを宣言するだけで、戻り型が何であるかを定義する->
という問題を回避できます(およびがsの場合、コンパイル時にとして解決されます)。x
y
int
delctype(x+y)
int
seq<0, 1...N>
次に、適切な使用法を提供する関数でラップしますgens<N>::type
。
template <template <typename ...> class Tup1,
template <typename ...> class Tup2,
typename ...A, typename ...B>
auto tuple_zip(Tup1<A...> t1, Tup2<B...> t2) ->
decltype(tuple_zip_helper(t1, t2, typename gens<sizeof...(A)>::type() )) {
static_assert(sizeof...(A) == sizeof...(B), "The tuple sizes must be the same");
return tuple_zip_helper( t1, t2, typename gens<sizeof...(A)>::type() );
}
これで、質問で指定されているとおりに使用できます。
int main() {
auto tup1 = std::make_tuple(1, 'b', -10);
auto tup2 = std::make_tuple(2.5, 2, std::string("even strings?!"));
std::tuple<
std::pair<int, double>,
std::pair<char, int>,
std::pair<int, std::string> > x = tuple_zip( tup1, tup2 );
// this is also equivalent:
// auto x = tuple_zip( tup1, tup2 );
return 0;
}
最後に、<<
演算子をstd::pair
指定すると、上記で定義した印刷関数を使用して、zip形式の結果を印刷できます。
template <typename A, typename B>
std::ostream & operator << (std::ostream & os, const std::pair<A, B> & pair) {
os << "pair("<< pair.first << "," << pair.second << ")";
return os;
}
int main() {
auto tup1 = std::make_tuple(1, 'b', -10);
auto tup2 = std::make_tuple(2.5, 2, std::string("even strings?!"));
auto x = tuple_zip( tup1, tup2 );
std::cout << "zipping: ";
print(tup1);
std::cout << "with : ";
print(tup2);
std::cout << "yields : ";
print(x);
return 0;
}
出力は次のとおりです。
圧縮:1 b 10
with:2.5 2偶数文字列?!
収量:ペア(1,2.5)ペア(b、2)ペア(10、偶数文字列?!)
のようstd::array
に、はコンパイル時に定義されるため、より最適化可能なコードを生成するために使用できます(およびstd::tuple
のようなコンテナーと比較して、コンパイル時に多くの情報がわかります)。そのため、少し手間がかかることもありますが、それを使用して高速で巧妙なコードを作成できることもあります。ハッピーハッキング!std::vector
std::list
編集:
要求に応じて、さまざまなサイズのタプルを許可し、nullポインターでパディングします。
template <typename T, std::size_t N, std::size_t ...S>
auto array_to_tuple_helper(const std::array<T, N> & arr, seq<S...> s) -> decltype(std::make_tuple(arr[S]...)) {
return std::make_tuple(arr[S]...);
}
template <typename T, std::size_t N>
auto array_to_tuple(const std::array<T, N> & arr) -> decltype( array_to_tuple_helper(arr, typename gens<N>::type()) ) {
return array_to_tuple_helper(arr, typename gens<N>::type());
}
template <std::size_t N, template <typename ...> class Tup, typename ...A>
auto pad(Tup<A...> tup) -> decltype(tuple_cat(tup, array_to_tuple(std::array<std::nullptr_t, N>()) )) {
return tuple_cat(tup, array_to_tuple(std::array<std::nullptr_t, N>()) );
}
#define EXTENSION_TO_FIRST(first,second) ((first)>(second) ? (first)-(second) : 0)
template <template <typename ...> class Tup1, template <typename ...> class Tup2, typename ...A, typename ...B>
auto pad_first(Tup1<A...> t1, Tup2<B...> t2) -> decltype( pad<EXTENSION_TO_FIRST(sizeof...(B), sizeof...(A)), Tup1, A...>(t1) ) {
return pad<EXTENSION_TO_FIRST(sizeof...(B), sizeof...(A)), Tup1, A...>(t1);
}
template <template <typename ...> class Tup1, template <typename ...> class Tup2, typename ...A, typename ...B>
auto diff_size_tuple_zip(Tup1<A...> t1, Tup2<B...> t2) ->
decltype( tuple_zip( pad_first(t1, t2), pad_first(t2, t1) ) ) {
return tuple_zip( pad_first(t1, t2), pad_first(t2, t1) );
}
ところで、便利なprint
関数を使用するには、これが必要になります。
std::ostream & operator << (std::ostream & os, std::nullptr_t) {
os << "null_ptr";
return os;
}