My question is simple, see example:
std::array<int,6> a = {{0,1,2,3,4,5}}; // -- given container.
auto F = []( int i ) { return i*i; }; // -- given function.
std::vector<int> v; // need create
// my solution:
v.reserve( a.size () );
for( std::size_t i = 0; i < a.size(); ++i )
v.push_back( F(a[i]) );
// but I need something like
std::vector<int>v( a.begin(), a.end(), <|applying each element to F|> );
Can I create container something like above not calling reserve explicitly and any reallocation?
EDIT:
The standard algorithm std::transform
does exactly this!
std::vector<int> v(a.size());
std::transform(
std::begin(a), std::end(a),
std::begin(v),
F
);
You can start with an empty vector and use std::back_inserter
, if you like:
std::vector<int> v;
std::transform(
std::begin(a), std::end(a),
std::back_inserter(v),
F
);
But you're subjecting yourself to needless re-allocations if you do that (unless you reserve
first, as in your original attempt). You can decide for yourself what your priority is.
Use std::transform
:
#include <algorithm> // std::transform
#include <iterator> // std::back_inserter
....
transform(a.begin(), a.end(), back_inserter(v), F);
You may want to call v.reserve(asize())
first to avoid re-allocations.
Another solution is to use boost::transform_iterator
. The benefit is that you can pass iterators to the container constructor. That avoids memory reallocations compared to when using std::back_inserter
or having to call reserve
or resize
on the destination. All in one statement:
std::vector<int> result(
boost::make_transform_iterator(std::begin(a), F)
, boost::make_transform_iterator(std::end(a), F)
);
You can achieve terser syntax though, like this:
std::vector<int> result(transform_range(a, F));
transform_range
implementation:
template<class Iterator>
struct AutoSequence
{
Iterator const beg_, end_;
template<class T>
operator std::vector<T>() const {
return {beg_, end_};
}
};
template<class Function, class InSeq>
auto transform_range(InSeq const& in) -> AutoSequence<decltype(boost::make_transform_iterator<Function>(in.begin()))> {
return {
boost::make_transform_iterator<Function>(std::begin(in))
, boost::make_transform_iterator<Function>(std::end(in))
};
}
template<class Function, class InSeq>
auto transform_range(InSeq const& in, Function&& f) -> AutoSequence<decltype(boost::make_transform_iterator(in.begin(), f))> {
return {
boost::make_transform_iterator(std::begin(in), f)
, boost::make_transform_iterator(std::end(in), f)
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With