TMP: как обобщить декартово произведение векторов?

Существует отличное C ++-решение (фактически 2 решения: рекурсивное и нерекурсивное), в декартово произведение вектора целых векторов . Для иллюстрации / простоты давайте просто сосредоточимся на нерекурсивной версии .

Мой вопрос в том, как можно обобщить этот код с шаблонами, чтобы взять std::tuple однородных векторов, который выглядит так:

{{2,5,9},{"foo","bar"}}

и порождают однородный вектор tuple

{{2,"foo"},{2,"bar"},{5,"foo"},{5,"bar"},{9,"foo"},{9,"bar"}}

Если это облегчит жизнь, допустим, что внутренние векторы на входе являются однородными. Поэтому входные данные, подобные этому, не допускаются : {{5,"baz"}{'c',-2}}

EDIT изменил входной сигнал от зубчатого вектора на кортеж

Упрощенное рекурсивное решение. Он принимает векторы как аргументы функции, а не как кортеж. Эта версия не создает временные кортежи, но вместо этого использует lambdas. Теперь он не делает ненужных копий / движений и, кажется, успешно оптимизирован.

 #include #include // cross_imp(f, v...) means "do `f` for each element of cartesian product of v..." template inline void cross_imp(F f) { f(); } template inline void cross_imp(F f, std::vector const& h, std::vector const&... t) { for(H const& he: h) cross_imp([&](Ts const&... ts){ f(he, ts...); }, t...); } template std::vector> cross(std::vector const&... in) { std::vector> res; cross_imp([&](Ts const&... ts){ res.emplace_back(ts...); }, in...); return res; } #include int main() { std::vector is = {2,5,9}; std::vector cps = {"foo","bar"}; std::vector ds = {1.5, 3.14, 2.71}; auto res = cross(is, cps, ds); for(auto& a: res) { std::cout << '{' << std::get<0>(a) << ',' << std::get<1>(a) << ',' << std::get<2>(a) << "}\n"; } } 

Прошло некоторое время с тех пор, как я это делал, но вот первая попытка. Без сомнения, это можно улучшить.

 template class DynamicTupleGetter { typedef typename std::tuple_element::type RetType; public: static RetType get(unsigned dynIndex, const T& tupleInstance) { const RetType& ret = std::get(tupleInstance); if (fixedIndex == dynIndex) return ret; return DynamicTupleGetter::get(dynIndex, tupleInstance); } }; template class DynamicTupleGetter<0, T> { typedef typename std::tuple_element<0, T>::type RetType; public: static RetType get(unsigned dynIndex, const T& tupleInstance) { assert(dynIndex == 0); return std::get<0>(tupleInstance); } }; template struct Converter { typedef typename std::tuple_element<0, Source>::type Zeroth; typedef typename std::tuple_element<1, Source>::type First; static const size_t size0 = std::tuple_size::value; static const size_t size1 = std::tuple_size::value; static const size_t outerProductSize = size0 * size1; typedef typename std::tuple_element<0, Zeroth>::type BaseType0; typedef typename std::tuple_element<0, First>::type BaseType1; typedef typename std::tuple EntryType; typedef std::array DestinationType; DestinationType create(const Source& source) { Zeroth zeroth = std::get<0>(source); First first = std::get<1>(source); typedef typename DynamicTupleGetter ZerothGetter; typedef typename DynamicTupleGetter FirstGetter; DestinationType result; size_t resultIndex = 0; for(size_t i = 0; i < size0; ++i) for(size_t j = 0; j < size1; ++j) { std::get<0>(result[resultIndex]) = ZerothGetter::get(i, zeroth) ; std::get<1>(result[resultIndex]) = FirstGetter::get(j, first); ++resultIndex; } return result; } }; template void create(const T& source) { Converter converter; Converter::DestinationType result = converter.create(source); std::cout << std::get<0>(std::get<3>(result)) << "," << std::get<1>(std::get<3>(result)) << std::endl; } auto intPart = std::make_tuple(2,5,9); auto stringPart = std::make_tuple("foo","bar"); auto source = std::make_tuple(intPart, stringPart); void f() { create(source); } 
Давайте будем гением компьютера.