Here's a rather straightforward and boring implementation.
#include <cstdlib>
#include <iostream>
namespace ct {
template <typename, typename>
struct pair {};
template <typename... a>
struct list {};
template <typename a, typename... b>
struct cons_t;
template <typename a>
struct cons_t<a, list<>>
{
using type = list<a>;
};
template <typename a, typename bh, typename... bt>
struct cons_t<a, list<bh, bt...>>
{
using type = list<a, bh, bt...>;
};
template <typename a, typename... b>
struct concat_t;
template <typename b>
struct concat_t<list<>, b>
{
using type = b;
};
template <typename b, typename ah, typename... at>
struct concat_t<list<ah, at...>, b>
{
using type = typename cons_t<ah, typename concat_t<list<at...>, b>::type>::type;
};
template <typename a, typename b>
using cons = typename cons_t<a,b>::type;
template <typename a, typename b>
using concat = typename concat_t<a,b>::type;
template <typename a, typename b>
struct cartesian1;
template <typename a>
struct cartesian1<a, list<>>
{
using type = list<>;
};
template <typename a, typename bh, typename... bt>
struct cartesian1<a, list<bh, bt...>>
{
using type = cons<pair<a, bh>, typename cartesian1<a, list<bt...>>::type>;
};
template <typename a, typename b>
struct cartesian_t;
template <typename a>
struct cartesian_t<list<>, a>
{
using type = list<>;
};
template <typename ah, typename b, typename... at>
struct cartesian_t<list<ah, at...>, b>
{
using type = concat<typename cartesian1<ah, b>::type,
typename cartesian_t<list<at...>, b>::type>;
};
template <typename a, typename b>
using cartesian = typename cartesian_t<a,b>::type;
template <size_t x>
struct val {};
template <size_t... a>
struct vlist {};
template <typename t>
struct vlist2list_t;
template <>
struct vlist2list_t<vlist<>>
{
using type = list<>;
};
template <size_t ah, size_t... at>
struct vlist2list_t<vlist<ah, at...>>
{
using type = cons<val<ah>, typename vlist2list_t<vlist<at...>>::type>;
};
template <typename a>
using vlist2list = typename vlist2list_t<a>::type;
template <size_t...a>
using vl = vlist2list<vlist<a...>>;
template<size_t x1, size_t x2>
std::ostream& operator<< (std::ostream& s,
pair<val<x1>, val<x2>> z)
{
s << "(" << x1 << "," << x2 << ")";
return s;
}
std::ostream& operator<< (std::ostream& s,
list<> z)
{
s << "[]";
}
template <typename ah, typename... at>
std::ostream& operator<< (std::ostream& s,
list<ah, at...> z)
{
s << ah() << ":" << list<at...>();
}
}
int main ()
{
using a = ct::cartesian<ct::vl<1,2,3>, ct::vl<4,5,6>>;
std::cout << a() << std::endl;
}