How to implement something like std::copy_if but apply a function before inserting into a different container

  • A+
Category:Languages

Full disclosure, this may be a hammer and nail situation trying to use STL algorithms when none are needed. I have seen a reappearing pattern in some C++14 code I am working with. We have a container that we iterate through, and if the current element matches some condition, then we copy one of the elements fields to another container.

The pattern is something like:

 for (auto it = std::begin(foo); it!=std::end(foo); ++it){     auto x = it->Some_member;     // Note, the check usually uses the field would add to the new container.      if(f(x) && g(x)){        bar.emplace_back(x);     }   } 

The idea is almost an accumulate where the function being applied does not always return a value. I can only think of a solutions that either

Is this even a good idea?

 


A quite general solution to your issue would be the following (working example):

#include <iostream> #include <vector> using namespace std;  template<typename It, typename MemberType, typename Cond, typename Do> void process_filtered(It begin, It end, MemberType iterator_traits<It>::value_type::*ptr, Cond condition, Do process) {     for(It it = begin; it != end; ++it)     {         if(condition((*it).*ptr))         {             process((*it).*ptr);         }     } }  struct Data {     int x;     int y; };  int main() {     // thanks to iterator_traits, vector could also be an array;     // kudos to @Yakk-AdamNevraumont     vector<Data> lines{{1,2},{4,3},{5,6}};      // filter even numbers from Data::x and output them     process_filtered(std::begin(lines), std::end(lines), &Data::x, [](int n){return n % 2 == 0;}, [](int n){cout << n;});      // output is 4, the only x value that is even      return 0; } 

It does not use STL, that is right, but you merely pass an iterator pair, the member to lookup and two lambdas/functions to it that will first filter and second use the filtered output, respectively.

I like your general solutions but here you do not need to have a lambda that extracts the corresponding attribute.

Clearly, the code can be refined to work with const_iterator but for a general idea, I think, it should be helpful. You could also extend it to have a member function that returns a member attribute instead of a direct member attribute pointer, if you'd like to use this method for encapsulated classes.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: