Ответ 1
Во-первых, быстрый обзор массивов индексов:
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 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
s, так что 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
in gens<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
и снабжен ->
после определения параметров). Это позволяет нам избежать проблемы даже определения того, каков будет тип возврата, просто объявив, что он возвращает выражение, используемое в теле функции (если x
и y
являются int
s, 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
, вы можете использовать указанную выше функцию печати для печати заархивированного результата:
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;
}
Вывод:
zipping: 1 b 10
с: 2.5 2 даже строки!
дает: пара (1,2,5) пара (b, 2) пара (10, четные строки?!)
Подобно std::array
, std::tuple
определяется во время компиляции, поэтому его можно использовать для создания более оптимизируемого кода (более подробная информация известна во время компиляции по сравнению с контейнерами типа std::vector
и std::list
). Поэтому, хотя иногда это немного работает, вы иногда можете использовать его для быстрого и умного кода. Счастливый взлом!
Edit:
В соответствии с запросом, позволяя кортежи разных размеров и дополнений с нулевыми указателями:
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) );
}
И BTW, вам понадобится это сейчас, чтобы использовать нашу удобную функцию print
:
std::ostream & operator << (std::ostream & os, std::nullptr_t) {
os << "null_ptr";
return os;
}