TMP:如何推广vector的笛卡尔乘积?
有一个优秀的C ++解决scheme(实际上是2个解决scheme:recursion和非recursion)到整数向量向量的笛卡尔乘积 。 为了说明/简单起见,让我们只关注非recursion版本 。
我的问题是,如何可以推广这个代码与模板采取一个std::tuple
同构向量std::tuple
看起来像这样:
{{2,5,9},{"foo","bar"}}
并生成一个tuple
向量
{{2,"foo"},{2,"bar"},{5,"foo"},{5,"bar"},{9,"foo"},{9,"bar"}}
如果它使生活更容易,让我们假设input中的内部向量是均匀的。 所以这样的input是不允许的 : {{5,"baz"}{'c',-2}}
编辑改变input从锯齿状vector到元组
更简单的recursion解决scheme。 它将向量作为函数参数,而不是元组。 这个版本不会构build临时元组,而是使用lambdas代替。 现在它没有任何不必要的副本/移动,似乎得到了优化成功。
#include<tuple> #include<vector> // cross_imp(f, v...) means "do `f` for each element of cartesian product of v..." template<typename F> inline void cross_imp(F f) { f(); } template<typename F, typename H, typename... Ts> inline void cross_imp(F f, std::vector<H> const& h, std::vector<Ts> const&... t) { for(H const& he: h) cross_imp([&](Ts const&... ts){ f(he, ts...); }, t...); } template<typename... Ts> std::vector<std::tuple<Ts...>> cross(std::vector<Ts> const&... in) { std::vector<std::tuple<Ts...>> res; cross_imp([&](Ts const&... ts){ res.emplace_back(ts...); }, in...); return res; } #include<iostream> int main() { std::vector<int> is = {2,5,9}; std::vector<char const*> cps = {"foo","bar"}; std::vector<double> 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<unsigned fixedIndex, class T> class DynamicTupleGetter { typedef typename std::tuple_element<fixedIndex, T>::type RetType; public: static RetType get(unsigned dynIndex, const T& tupleInstance) { const RetType& ret = std::get<fixedIndex>(tupleInstance); if (fixedIndex == dynIndex) return ret; return DynamicTupleGetter<fixedIndex - 1, T>::get(dynIndex, tupleInstance); } }; template<class T> 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<class Source> 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<Zeroth>::value; static const size_t size1 = std::tuple_size<First>::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<BaseType0, BaseType1> EntryType; typedef std::array<EntryType, outerProductSize> DestinationType; DestinationType create(const Source& source) { Zeroth zeroth = std::get<0>(source); First first = std::get<1>(source); typedef typename DynamicTupleGetter<size0 -1, Zeroth> ZerothGetter; typedef typename DynamicTupleGetter<size1 -1, First> 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<class T> void create(const T& source) { Converter<T> converter; Converter<T>::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); }