从构造函数参数的组合生成变量类型的对象
Generating objects of variable types from combinations of constructor arguments
假设我有类型 A
、B
和构造函数 A(int a, double b, std::string c)
、B(double a, int b)
。
我知道如何定义一个通过可变参数模板实例化 A
或 B
的函数。
有什么方法可以设计一个 function/macro/type 类型 T
和 T
的构造函数参数的一系列可能性向量,它为我提供了所有可能的对象?
例如,如果我为 <A, {2, 5, 6}, {2.44, 3.14}, {"yes", "no"}>
使用这个神奇的构造,它应该提供对象:
A(2, 2.44, "yes")
A(2, 2.44, "no")
A(2, 3.14, "yes")
...
A(6, 3.14, "no")
同样适用于 B
或任何其他类型,而无需返工魔法结构。
例如,这在 Python 中非常简单,但我不知道在 C++ 中是否可行。
您正在寻找的是 "factory",它可能调用也可能不调用一个或多个构造函数,具体取决于您的实现。当然,如果 A 和 B 是同一父代的后代 class(或者其中一个是另一个父代的后代)。
编写一个组工厂来构建您需要的任何对象的集合也是可行的。当然,决定如何存储指向新建对象的指针将是一个设计考虑因素。
我想说得更具体一些,但我至少需要看一下您所指的 Python 代码的示例。
这使用 std::experimental::array_view
来提高效率。您可以以一些运行时成本将其替换为 std::vector
,或者以一些清晰度成本将其替换为一对 iterators/pointers。
template<class T>
using array_view = std::experimental::array_view<T>;
using indexes = array_view<std::size_t>;
这会迭代每个元素的叉积 <
索引中的相应索引。所以 {3,3,2}
作为 is
遍历 {0,0,0}
然后 {0,0,1}
一直到 {2,2,1}
.
template<class F>
void for_each_cartesian_product(
indexes is,
F&& f,
std::vector<std::size_t>& result
) {
if (is.empty()) {
f(result);
return;
}
auto max_index = is.front();
for (std::size_t i = 0; i < max_index; ++i) {
result.push_back(i);
for_each_cartesian_product( {is.begin()+1, is.end()}, f, result );
result.pop_back();
}
}
template<class F>
void for_each_cartesian_product(
indexes is,
F&& f
) {
std::vector<size_t> buffer;
for_each_cartesian_product( is, f, buffer );
}
然后我们只填充我们的索引:
template<class...Ts>
std::vector<std::size_t> get_indexes( std::vector<Ts> const&... vs ) {
return {vs.size()...};
}
接下来,我们只需要把我们的参数放在一个向量中,然后使用索引从每个向量中获取元素并将它们传递给 A
进行构造。
template<class T, std::size_t...Is, class...Args>
std::vector<T> make_stuff( std::index_sequence<Is...>, std::vector<Args>const&... args ) {
std::vector<T> retval;
for_each_cartesian_product(
get_indexes(args...),
[&](auto&& index){
retval.emplace_back( args[ index[Is] ]... );
}
);
return retval;
}
template<class T, class...Args>
std::vector<T> make_stuff( std::vector<Args>const&... args ) {
return make_stuff<T>( std::index_sequence_for<Args...>{}, args... );
}
鲍勃是你的叔叔。
生成的 A
可能会被移动。
在编译时使用编译时已知数组也可以做到这一点。
index_sequence_for
和 index_sequence
是 C++14,但在 C++11 中很容易实现。关于栈溢出的例子很多。
以上代码未编译
这是 的 C++14 版本(应归功于此),它可以同时使用 homogeneous 容器(包含单一类型)和 异构 std::tuple
容器(可能包含不同类型的对象)作为输入。这允许您混合和匹配参数类型以调用多个构造函数。
这可以在 C++11 中实现,但需要更多代码来模拟可变参数的通用 lambda 捕获。
#include <algorithm>
#include <tuple>
#include <vector>
template <typename Ts, size_t... Is, typename F>
void for_each_impl(Ts const& t, std::index_sequence<Is...> is, F const& f)
{
using expand = int[];
expand{ (f(std::get<Is>(t)), 0)... };
}
template <typename... Ts, typename F>
void for_each(std::tuple<Ts...> const& t, F const& f)
{
for_each_impl(t, std::make_index_sequence<sizeof...(Ts)>(), f);
}
template <typename T, typename F>
void for_each(T const& t, F const& f)
{
std::for_each(std::begin(t), std::end(t), f);
}
template <std::size_t Index, std::size_t Count>
struct cartesian_builder
{
template <typename T, typename Sets, typename... Args>
static void make(std::vector<T> &v, Sets const& sets, Args const&... args)
{
for_each(std::get<Index>(sets), [&](auto& arg) {
cartesian_builder<Index + 1, Count>::make(v, sets, args..., arg);
});
}
};
template <std::size_t Count>
struct cartesian_builder<Count, Count> {
template <typename T, typename Sets, typename... Args>
static void make(std::vector<T>& v, Sets const&, Args const&... args)
{
v.emplace_back(args...);
}
};
template <>
struct cartesian_builder<0, 0> {
template <typename T, typename Sets, typename... Args>
static void make(std::vector<T>&, Sets const&, Args const&...)
{
}
};
template <typename T, typename... Sets>
static std::vector<T> make_cartesian_product(Sets const&... sets)
{
std::vector<T> v;
cartesian_builder<0, sizeof...(sets)>::make(v, std::tie(sets...));
return v;
}
用法示例:
#include <iostream>
#include <set>
#include <string>
#include <vector>
int main()
{
struct A
{
int a;
double b;
std::string c;
A(std::string a, double b, std::string c) :
a(0), b(b), c(a + " " + c)
{
}
A(int a, double b, std::string c) :
a(a), b(b), c(c)
{
}
};
std::vector<A> objects = make_cartesian_product<A>(
std::make_tuple(2, "maybe", 6),
std::set<double>{2.44, 3.14},
std::vector<char const*>{"yes", "no"}
);
for (auto& o : objects)
{
std::cout << "(" << o.a << ", " << o.b << ", " << o.c << ")\n";
}
}
这里有一个比较简单的做法:
#include <vector>
#include <string>
#include <iostream>
#include <tuple>
using std::vector;
using std::cout;
using std::tie;
template <size_t index,size_t count>
struct Maker {
template <typename T,typename Tuple,typename...Args>
static void make(vector<T> &v,const Tuple &tup,Args &...args)
{
for (auto &x : std::get<index>(tup)) {
Maker<index+1,count>::make(v,tup,args...,x);
}
}
};
template<size_t index>
struct Maker<index,index> {
template <typename T,typename Tuple,typename...Args>
static void make(vector<T> &v,const Tuple &,Args &...args)
{
v.push_back(T(args...));
}
};
template <typename T,typename...Ts>
static vector<T> combinations(const Ts &...args)
{
vector<T> v;
Maker<0,sizeof...(args)>::make(v,tie(args...));
return v;
}
int main()
{
struct A {
A(int,double,std::string) { }
};
struct B {
B(double,int) { }
};
vector<A> as =
combinations<A>(
vector<int>{2,5,6},
vector<double>{2.44,3.14},
vector<const char *>{"yes","no"}
);
vector<B> bs =
combinations<B>(
vector<double>{2.44,3.14},
vector<int>{2,5,6}
);
cout << "as.size()=" << as.size() << "\n";
cout << "bs.size()=" << bs.size() << "\n";
}
输出:
12
6
我的解决方案没有Yacc的优雅(毕竟我正在努力学习),但我认为它相对简单。
欢迎提出意见和报告缺陷。
#include <tuple>
#include <vector>
#include <complex>
#include <iostream>
#include <initializer_list>
template <std::size_t ... N, typename X, typename ... Al>
void cartHelp (std::vector<X> & v,
std::tuple<Al...> const & t)
{ v.emplace_back(std::get<N>(t)...); }
template <std::size_t ... N, typename X, typename ... Al1, typename L,
typename ... Al2>
void cartHelp (std::vector<X> & v,
std::tuple<Al1...> const & t,
std::initializer_list<L> const & l,
std::initializer_list<Al2> const & ... al2)
{
for ( auto const & elem : l )
cartHelp<N..., sizeof...(N)>(v, std::tuple_cat(t, std::tie(elem)),
al2...);
}
template <typename X, typename L, typename ... Al>
std::vector<X> cartesian (std::initializer_list<L> const & l,
std::initializer_list<Al> const & ... al)
{
std::vector<X> v;
for ( auto const & elem : l )
cartHelp<0>(v, std::tie(elem), al...);
return v;
}
int main()
{
auto v1 = cartesian<int>({1,2});
std::cout << "--- v1.size --- " << v1.size() << "\n";
std::cout << "v1";
for ( auto const & elem : v1 )
std::cout << '[' << elem << ']';
std::cout << '\n';
auto v2 = cartesian<std::complex<double>>
({1.2,2.3,3.4}, {11.11, 22.22, 33.33});
std::cout << "--- v2.size --- " << v2.size() << "\n";
std::cout << "v2";
for ( auto const & elem : v2 )
std::cout << '[' << elem << ']';
std::cout << '\n';
auto v3 = cartesian<std::tuple<int, double, std::string>>
({1, 2, 3, 4, 5}, {0.1, 0.2, 0.3, 0.4},
{std::string("aaa"), std::string("bbb"), std::string("ccc")});
std::cout << "--- v3.size --- " << v3.size() << "\n";
std::cout << "v3";
for ( auto const & elem : v3 )
std::cout << '[' << std::get<0>(elem) << ',' << std::get<1>(elem)
<< ',' << std::get<2>(elem) << ']';
std::cout << '\n';
return 0;
}
输出:
--- v1.size --- 2
v1[1][2]
--- v2.size --- 9
v2[(1.2,11.11)][(1.2,22.22)][(1.2,33.33)][(2.3,11.11)][(2.3,22.22)][(2.3,33.33)][(3.4,11.11)][(3.4,22.22)][(3.4,33.33)]
--- v3.size --- 60
v3[1,0.1,aaa][1,0.1,bbb][1,0.1,ccc][1,0.2,aaa][1,0.2,bbb][1,0.2,ccc][1,0.3,aaa][1,0.3,bbb][1,0.3,ccc][1,0.4,aaa][1,0.4,bbb][1,0.4,ccc][2,0.1,aaa][2,0.1,bbb][2,0.1,ccc][2,0.2,aaa][2,0.2,bbb][2,0.2,ccc][2,0.3,aaa][2,0.3,bbb][2,0.3,ccc][2,0.4,aaa][2,0.4,bbb][2,0.4,ccc][3,0.1,aaa][3,0.1,bbb][3,0.1,ccc][3,0.2,aaa][3,0.2,bbb][3,0.2,ccc][3,0.3,aaa][3,0.3,bbb][3,0.3,ccc][3,0.4,aaa][3,0.4,bbb][3,0.4,ccc][4,0.1,aaa][4,0.1,bbb][4,0.1,ccc][4,0.2,aaa][4,0.2,bbb][4,0.2,ccc][4,0.3,aaa][4,0.3,bbb][4,0.3,ccc][4,0.4,aaa][4,0.4,bbb][4,0.4,ccc][5,0.1,aaa][5,0.1,bbb][5,0.1,ccc][5,0.2,aaa][5,0.2,bbb][5,0.2,ccc][5,0.3,aaa][5,0.3,bbb][5,0.3,ccc][5,0.4,aaa][5,0.4,bbb][5,0.4,ccc]
自动可变参数 lambdas (C++14) 可用于存储参数引用而不是中间索引。
代码变的很简单,性能还不确定:
/** calls f for each element in c */
template <typename F, typename C>
void for_each_product(F&& f, const C& c) {
for (auto& e: c) f(e);
}
/** calls f for each cartesian product from c,cs containers */
template <typename F, typename C, typename... Cs>
void for_each_product(F&& f, const C& c, const Cs&... cs) {
for (auto& e: c)
for_each_product(
[&f, &e](auto&&... args) {
f(e, args...);
}, cs...);
}
就这些:-)
使用方法如下:
#include <vector>
#include <string>
struct A {
int i;
double d;
std::string s;
A(int _i, double _d, std::string _s): i(_i), d(_d), s(_s) {}
};
int main() {
auto vi = std::vector<int>{2, 5, 6};
auto vd = std::vector<double>{2.44, 3.14};
auto vs = std::vector<std::string>{"yes", "no"};
auto va = std::vector<A>();
for_each_product([&va](auto&&... args){
va.emplace_back(args...);
}, vi, vd, vs);
}
对初始化列表的额外支持:
/** calls f for each element in c */
template <typename F, typename T>
void for_each_product(F&& f, const std::initializer_list<T>& c){
for (auto& e: c) f(e);
}
/** calls f for each cartesian product from c,cs containers */
template <typename F, typename T, typename... Ts>
void for_each_product(F&& f, const std::initializer_list<T>& c,
const std::initializer_list<Ts>&... cs){
for (auto& e: c)
for_each_product(
[&f,&e](auto&&... args) {
f(e, args...);
}, cs...);
}
然后可以这样使用:
for_each_product([&va](auto&&... args){ va.emplace_back(args...); },
{2, 5, 6},
{2.44, 3.14},
{"yes", "no"});
但是初始化列表不与容器混合,不知道如何解决这个问题。
假设我有类型 A
、B
和构造函数 A(int a, double b, std::string c)
、B(double a, int b)
。
我知道如何定义一个通过可变参数模板实例化 A
或 B
的函数。
有什么方法可以设计一个 function/macro/type 类型 T
和 T
的构造函数参数的一系列可能性向量,它为我提供了所有可能的对象?
例如,如果我为 <A, {2, 5, 6}, {2.44, 3.14}, {"yes", "no"}>
使用这个神奇的构造,它应该提供对象:
A(2, 2.44, "yes")
A(2, 2.44, "no")
A(2, 3.14, "yes")
...
A(6, 3.14, "no")
同样适用于 B
或任何其他类型,而无需返工魔法结构。
例如,这在 Python 中非常简单,但我不知道在 C++ 中是否可行。
您正在寻找的是 "factory",它可能调用也可能不调用一个或多个构造函数,具体取决于您的实现。当然,如果 A 和 B 是同一父代的后代 class(或者其中一个是另一个父代的后代)。
编写一个组工厂来构建您需要的任何对象的集合也是可行的。当然,决定如何存储指向新建对象的指针将是一个设计考虑因素。
我想说得更具体一些,但我至少需要看一下您所指的 Python 代码的示例。
这使用 std::experimental::array_view
来提高效率。您可以以一些运行时成本将其替换为 std::vector
,或者以一些清晰度成本将其替换为一对 iterators/pointers。
template<class T>
using array_view = std::experimental::array_view<T>;
using indexes = array_view<std::size_t>;
这会迭代每个元素的叉积 <
索引中的相应索引。所以 {3,3,2}
作为 is
遍历 {0,0,0}
然后 {0,0,1}
一直到 {2,2,1}
.
template<class F>
void for_each_cartesian_product(
indexes is,
F&& f,
std::vector<std::size_t>& result
) {
if (is.empty()) {
f(result);
return;
}
auto max_index = is.front();
for (std::size_t i = 0; i < max_index; ++i) {
result.push_back(i);
for_each_cartesian_product( {is.begin()+1, is.end()}, f, result );
result.pop_back();
}
}
template<class F>
void for_each_cartesian_product(
indexes is,
F&& f
) {
std::vector<size_t> buffer;
for_each_cartesian_product( is, f, buffer );
}
然后我们只填充我们的索引:
template<class...Ts>
std::vector<std::size_t> get_indexes( std::vector<Ts> const&... vs ) {
return {vs.size()...};
}
接下来,我们只需要把我们的参数放在一个向量中,然后使用索引从每个向量中获取元素并将它们传递给 A
进行构造。
template<class T, std::size_t...Is, class...Args>
std::vector<T> make_stuff( std::index_sequence<Is...>, std::vector<Args>const&... args ) {
std::vector<T> retval;
for_each_cartesian_product(
get_indexes(args...),
[&](auto&& index){
retval.emplace_back( args[ index[Is] ]... );
}
);
return retval;
}
template<class T, class...Args>
std::vector<T> make_stuff( std::vector<Args>const&... args ) {
return make_stuff<T>( std::index_sequence_for<Args...>{}, args... );
}
鲍勃是你的叔叔。
生成的 A
可能会被移动。
在编译时使用编译时已知数组也可以做到这一点。
index_sequence_for
和 index_sequence
是 C++14,但在 C++11 中很容易实现。关于栈溢出的例子很多。
以上代码未编译
这是 std::tuple
容器(可能包含不同类型的对象)作为输入。这允许您混合和匹配参数类型以调用多个构造函数。
这可以在 C++11 中实现,但需要更多代码来模拟可变参数的通用 lambda 捕获。
#include <algorithm>
#include <tuple>
#include <vector>
template <typename Ts, size_t... Is, typename F>
void for_each_impl(Ts const& t, std::index_sequence<Is...> is, F const& f)
{
using expand = int[];
expand{ (f(std::get<Is>(t)), 0)... };
}
template <typename... Ts, typename F>
void for_each(std::tuple<Ts...> const& t, F const& f)
{
for_each_impl(t, std::make_index_sequence<sizeof...(Ts)>(), f);
}
template <typename T, typename F>
void for_each(T const& t, F const& f)
{
std::for_each(std::begin(t), std::end(t), f);
}
template <std::size_t Index, std::size_t Count>
struct cartesian_builder
{
template <typename T, typename Sets, typename... Args>
static void make(std::vector<T> &v, Sets const& sets, Args const&... args)
{
for_each(std::get<Index>(sets), [&](auto& arg) {
cartesian_builder<Index + 1, Count>::make(v, sets, args..., arg);
});
}
};
template <std::size_t Count>
struct cartesian_builder<Count, Count> {
template <typename T, typename Sets, typename... Args>
static void make(std::vector<T>& v, Sets const&, Args const&... args)
{
v.emplace_back(args...);
}
};
template <>
struct cartesian_builder<0, 0> {
template <typename T, typename Sets, typename... Args>
static void make(std::vector<T>&, Sets const&, Args const&...)
{
}
};
template <typename T, typename... Sets>
static std::vector<T> make_cartesian_product(Sets const&... sets)
{
std::vector<T> v;
cartesian_builder<0, sizeof...(sets)>::make(v, std::tie(sets...));
return v;
}
用法示例:
#include <iostream>
#include <set>
#include <string>
#include <vector>
int main()
{
struct A
{
int a;
double b;
std::string c;
A(std::string a, double b, std::string c) :
a(0), b(b), c(a + " " + c)
{
}
A(int a, double b, std::string c) :
a(a), b(b), c(c)
{
}
};
std::vector<A> objects = make_cartesian_product<A>(
std::make_tuple(2, "maybe", 6),
std::set<double>{2.44, 3.14},
std::vector<char const*>{"yes", "no"}
);
for (auto& o : objects)
{
std::cout << "(" << o.a << ", " << o.b << ", " << o.c << ")\n";
}
}
这里有一个比较简单的做法:
#include <vector>
#include <string>
#include <iostream>
#include <tuple>
using std::vector;
using std::cout;
using std::tie;
template <size_t index,size_t count>
struct Maker {
template <typename T,typename Tuple,typename...Args>
static void make(vector<T> &v,const Tuple &tup,Args &...args)
{
for (auto &x : std::get<index>(tup)) {
Maker<index+1,count>::make(v,tup,args...,x);
}
}
};
template<size_t index>
struct Maker<index,index> {
template <typename T,typename Tuple,typename...Args>
static void make(vector<T> &v,const Tuple &,Args &...args)
{
v.push_back(T(args...));
}
};
template <typename T,typename...Ts>
static vector<T> combinations(const Ts &...args)
{
vector<T> v;
Maker<0,sizeof...(args)>::make(v,tie(args...));
return v;
}
int main()
{
struct A {
A(int,double,std::string) { }
};
struct B {
B(double,int) { }
};
vector<A> as =
combinations<A>(
vector<int>{2,5,6},
vector<double>{2.44,3.14},
vector<const char *>{"yes","no"}
);
vector<B> bs =
combinations<B>(
vector<double>{2.44,3.14},
vector<int>{2,5,6}
);
cout << "as.size()=" << as.size() << "\n";
cout << "bs.size()=" << bs.size() << "\n";
}
输出:
12
6
我的解决方案没有Yacc的优雅(毕竟我正在努力学习),但我认为它相对简单。
欢迎提出意见和报告缺陷。
#include <tuple>
#include <vector>
#include <complex>
#include <iostream>
#include <initializer_list>
template <std::size_t ... N, typename X, typename ... Al>
void cartHelp (std::vector<X> & v,
std::tuple<Al...> const & t)
{ v.emplace_back(std::get<N>(t)...); }
template <std::size_t ... N, typename X, typename ... Al1, typename L,
typename ... Al2>
void cartHelp (std::vector<X> & v,
std::tuple<Al1...> const & t,
std::initializer_list<L> const & l,
std::initializer_list<Al2> const & ... al2)
{
for ( auto const & elem : l )
cartHelp<N..., sizeof...(N)>(v, std::tuple_cat(t, std::tie(elem)),
al2...);
}
template <typename X, typename L, typename ... Al>
std::vector<X> cartesian (std::initializer_list<L> const & l,
std::initializer_list<Al> const & ... al)
{
std::vector<X> v;
for ( auto const & elem : l )
cartHelp<0>(v, std::tie(elem), al...);
return v;
}
int main()
{
auto v1 = cartesian<int>({1,2});
std::cout << "--- v1.size --- " << v1.size() << "\n";
std::cout << "v1";
for ( auto const & elem : v1 )
std::cout << '[' << elem << ']';
std::cout << '\n';
auto v2 = cartesian<std::complex<double>>
({1.2,2.3,3.4}, {11.11, 22.22, 33.33});
std::cout << "--- v2.size --- " << v2.size() << "\n";
std::cout << "v2";
for ( auto const & elem : v2 )
std::cout << '[' << elem << ']';
std::cout << '\n';
auto v3 = cartesian<std::tuple<int, double, std::string>>
({1, 2, 3, 4, 5}, {0.1, 0.2, 0.3, 0.4},
{std::string("aaa"), std::string("bbb"), std::string("ccc")});
std::cout << "--- v3.size --- " << v3.size() << "\n";
std::cout << "v3";
for ( auto const & elem : v3 )
std::cout << '[' << std::get<0>(elem) << ',' << std::get<1>(elem)
<< ',' << std::get<2>(elem) << ']';
std::cout << '\n';
return 0;
}
输出:
--- v1.size --- 2
v1[1][2]
--- v2.size --- 9
v2[(1.2,11.11)][(1.2,22.22)][(1.2,33.33)][(2.3,11.11)][(2.3,22.22)][(2.3,33.33)][(3.4,11.11)][(3.4,22.22)][(3.4,33.33)]
--- v3.size --- 60
v3[1,0.1,aaa][1,0.1,bbb][1,0.1,ccc][1,0.2,aaa][1,0.2,bbb][1,0.2,ccc][1,0.3,aaa][1,0.3,bbb][1,0.3,ccc][1,0.4,aaa][1,0.4,bbb][1,0.4,ccc][2,0.1,aaa][2,0.1,bbb][2,0.1,ccc][2,0.2,aaa][2,0.2,bbb][2,0.2,ccc][2,0.3,aaa][2,0.3,bbb][2,0.3,ccc][2,0.4,aaa][2,0.4,bbb][2,0.4,ccc][3,0.1,aaa][3,0.1,bbb][3,0.1,ccc][3,0.2,aaa][3,0.2,bbb][3,0.2,ccc][3,0.3,aaa][3,0.3,bbb][3,0.3,ccc][3,0.4,aaa][3,0.4,bbb][3,0.4,ccc][4,0.1,aaa][4,0.1,bbb][4,0.1,ccc][4,0.2,aaa][4,0.2,bbb][4,0.2,ccc][4,0.3,aaa][4,0.3,bbb][4,0.3,ccc][4,0.4,aaa][4,0.4,bbb][4,0.4,ccc][5,0.1,aaa][5,0.1,bbb][5,0.1,ccc][5,0.2,aaa][5,0.2,bbb][5,0.2,ccc][5,0.3,aaa][5,0.3,bbb][5,0.3,ccc][5,0.4,aaa][5,0.4,bbb][5,0.4,ccc]
自动可变参数 lambdas (C++14) 可用于存储参数引用而不是中间索引。
代码变的很简单,性能还不确定:
/** calls f for each element in c */
template <typename F, typename C>
void for_each_product(F&& f, const C& c) {
for (auto& e: c) f(e);
}
/** calls f for each cartesian product from c,cs containers */
template <typename F, typename C, typename... Cs>
void for_each_product(F&& f, const C& c, const Cs&... cs) {
for (auto& e: c)
for_each_product(
[&f, &e](auto&&... args) {
f(e, args...);
}, cs...);
}
就这些:-)
使用方法如下:
#include <vector>
#include <string>
struct A {
int i;
double d;
std::string s;
A(int _i, double _d, std::string _s): i(_i), d(_d), s(_s) {}
};
int main() {
auto vi = std::vector<int>{2, 5, 6};
auto vd = std::vector<double>{2.44, 3.14};
auto vs = std::vector<std::string>{"yes", "no"};
auto va = std::vector<A>();
for_each_product([&va](auto&&... args){
va.emplace_back(args...);
}, vi, vd, vs);
}
对初始化列表的额外支持:
/** calls f for each element in c */
template <typename F, typename T>
void for_each_product(F&& f, const std::initializer_list<T>& c){
for (auto& e: c) f(e);
}
/** calls f for each cartesian product from c,cs containers */
template <typename F, typename T, typename... Ts>
void for_each_product(F&& f, const std::initializer_list<T>& c,
const std::initializer_list<Ts>&... cs){
for (auto& e: c)
for_each_product(
[&f,&e](auto&&... args) {
f(e, args...);
}, cs...);
}
然后可以这样使用:
for_each_product([&va](auto&&... args){ va.emplace_back(args...); },
{2, 5, 6},
{2.44, 3.14},
{"yes", "no"});
但是初始化列表不与容器混合,不知道如何解决这个问题。