Search code examples
c++c++11iteratorcontainersstl-algorithm

create container from another container, applying each element some function in c++


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:

  1. I want avoid reserve; because othercase my first solution is good for me :)
  2. I want avoid any resize; because it's initialzed each element by default ctor.
  3. I will use this in the real project which may included many 3-rd party libraries ( boost, Soft-STL, ...).

Solution

  • 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)
            };
    }