This little function can make object creation much easier. Sometimes you need to switch between representing your data as objects vs. having the members in separate containers.
Example:
class Foo {
public:
Foo(int a_, double b_): a(a_), b(b_){}
int a;
double b;
};
And then:
std::vector<int> as = {1,2,3,4,5};
std::vector<double> bs = {6.0,7.0,8.0,9.0,10.0};
std::vector<Foo> foos = zipConstruct<Foo>(as, bs);
cout << foos[3].a << ", " << foos[3].b << endl; // 4, 9
Using:
template <typename T, typename... Arg>
std::vector<T> zipConstruct(std::vector<Arg> const&... argVec)
{
std::vector<size_t> sizes = {argVec.size()...};
assert(std::all_of(sizes.begin(), sizes.end(), [&](size_t s){return s=sizes[0];}));
size_t size = sizes[0];
std::vector<T> result;
result.reserve(size);
for (size_t i = 0; i < size; ++i)
{
result.emplace_back((argVec[i])...);
}
return result;
}
Does this make sense?
1 Answer 1
I see one error:
assert(std::all_of(sizes.begin(), sizes.end(), [&](size_t s){return s=sizes[0];}));
^
This is an assignment, not a test. You probably meant s == sizes[0]
.
You can help this by passing s
as a const
value:
assert(std::all_of(sizes.begin(), sizes.end(), [&](size_t const s){return s == sizes[0];}));
Additionally, you may not mind destroying your old vectors (via move). So it may be worth looking at using move construction on the result
vector:
std::vector<T> zipConstruct(std::vector<Arg>&... argVec)
{ // ^^^ pass by reference as you may modify them
result.emplace_back(std::move(argVec[i])...);
// ^^^^^^^^^^ Add standard move to get move semantics.
Foo
just be astruct
? \$\endgroup\$