問題2

この前の問題2が少し気になっていた。

交互に要素を取ることで、2つのリストを結合する関数を記述せよ。例えば [a, b, c]と[1, 2, 3]という2つのリストを与えると、関数は [a, 1, b, 2, c, 3]を返す。

というやつである。

というわけで、std::array<char, 3>std::array<int, 3>を受け取ってstd::tuple<char, int, char, int, char, int>を返す関数を書いた。

std::tuple_catが非常に便利だということがわかった。また、単一の関数で再帰しようとしたところ、毎再帰ステップで返す型が異なるのでエラーが出た。あまり色々試す前にとっとと諦めてしまったので、また思い出したときに書いてみよう。

基本的には、

combine (x:xs) (y:ys) = combine xs ys ++ [x, y]

のようなことをしている。順番が逆だが。

#include <tuple>
#include <array>
#include <type_traits>

template<typename ... T_args>
struct join;

template<template<typename ... T> class pack, typename T, typename ... T_args>
struct join<T, pack<T_args...>>
{
    typedef pack<T, T_args...> type;
};


static_assert(std::is_same<typename join<int, std::tuple<char, double>>::type,
                           std::tuple<int, char, double>>::value, "join");

template<typename T1, typename T2, std::size_t N, typename ... T_args>
struct combine_type_impl
{
    typedef typename combine_type_impl<T1, T2, N-1, typename join<T1, typename join<T2, std::tuple<T_args...>>::type>::type>::type type;
};

template<typename T1, typename T2, std::size_t N, typename ... T_args>
struct combine_type_impl<T1, T2, N, std::tuple<T_args...>>
{
    typedef typename combine_type_impl<T1, T2, N-1, typename join<T1, typename join<T2, std::tuple<T_args...>>::type>::type>::type type;
};

template<typename T1, typename T2, typename ... T_args>
struct combine_type_impl<T1, T2, 0, std::tuple<T_args...>>
{
    typedef typename join<T1, typename join<T2, std::tuple<T_args...>>::type>::type type;
};

template<typename T1, typename T2, std::size_t N>
struct combine_type
{
    typedef typename combine_type_impl<T1, T2, N-1>::type type;
};

static_assert(std::is_same<typename combine_type<char, int, 3>::type,
                           std::tuple<char, int, char, int, char, int>>::value,
              "combine_type");

template<typename T1, typename T2, std::size_t N, std::size_t M>
struct combine_impl
{
    static typename combine_type<T1, T2, M+1>::type
    invoke(std::array<T1, N> lhs, std::array<T2, N> rhs)
    {
        return std::tuple_cat(combine_impl<T1, T2, N, M-1>::invoke(lhs, rhs),
                              std::make_tuple(lhs[M], rhs[M]));
    }
};

template<typename T1, typename T2, std::size_t N>
struct combine_impl<T1, T2, N, 0>
{
    static std::tuple<T1, T2>
    invoke(std::array<T1, N> lhs, std::array<T2, N> rhs)
    {
        return std::make_tuple(lhs[0], rhs[0]);
    }
};

template<typename T1, typename T2, std::size_t N>
typename combine_type<T1, T2, N>::type
combine(std::array<T1, N> lhs, std::array<T2, N> rhs)
{
    return combine_impl<T1, T2, N, N-1>::invoke(lhs, rhs);
}

#include <iostream>
int main()
{
    constexpr std::array<char, 3> ar1{{'a', 'b', 'c'}};
    constexpr std::array<int, 3>  ar2{{1, 2, 3}};
    typename combine_type<char, int, 3>::type t = combine(ar1, ar2);
    std::cout << std::get<0>(t) << ", "
              << std::get<1>(t) << ", "
              << std::get<2>(t) << ", "
              << std::get<3>(t) << ", "
              << std::get<4>(t) << ", "
              << std::get<5>(t) << std::endl;
    return 0;
}