Best way to emulate something like conditional_back_inserter?

I wanted to replace the loop with an algorithm in the following code

int numbers[] = { ... }; vector<int> output; for( int* it = numbers+from; it != numbers+to ; ++it ) { int square = func( *it ); if( predicate(square) ) { output.push_back(square); } } 

The program is designed to convert values ​​and copy them to the destination, if a condition occurs.

  • I could not use std::copy_if because this would not apply the conversion.
  • I could not use std::transform because it lacks a predicate
  • It is not recommended to even write transform_copy_if() because of the intermediate copy of the converted variable.

It seems like my only hope is to create a conditional_back_insert_iterator . Then I could have a pretty decent call like:

 int numbers[] = { ... }; vector<int> output; std::transform(numbers+from, numbers+to, conditional_back_inserter(predicate, output), func); 

Is this the best way to treat such cases? I couldn't even google for conditional inserts, so I'm worried that I'm wrong.

I could also suggest that I could implement an alternative solution such as

 std::copy_if( transform_iterator<func>(numbers+from), transform_iterator<func>(numbers+to), back_inserter(output) ); 

(which reminds me of the * filter_iterators * example in boost) but it does not give readability.

+6
source share
2 answers

I think creating your own iterator is the way to go:

 #include <iostream> #include <vector> #include <iterator> #include <functional> template<class T> class conditional_back_insert_iterator : public std::back_insert_iterator<std::vector<T>> { private: using Base = std::back_insert_iterator<std::vector<T>>; using Container = std::vector<T>; using value_type = typename Container::value_type; public: template<class F> conditional_back_insert_iterator(Container& other, F&& pred) : Base(other), c(other), predicate(std::forward<F>(pred)) { } conditional_back_insert_iterator<T>& operator*() { return *this; } conditional_back_insert_iterator<T>& operator=(const value_type& val) const { if (predicate(val)) c.push_back(val); return *this; } conditional_back_insert_iterator<T>& operator=(value_type&& val) const { if (predicate(val)) c.push_back(std::move(val)); return *this; } private: Container& c; std::function<bool (const value_type&)> predicate; }; template< class Container, class F, class value_type = typename Container::value_type > conditional_back_insert_iterator<value_type> conditional_back_inserter(Container& c, F&& predicate) { return conditional_back_insert_iterator<value_type>(c, std::forward<F>(predicate)); } int main() { std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9}; std::vector<int> to; auto is_even = [] (int x) { return (x % 2) == 0; }; std::copy(v.begin(), v.end(), conditional_back_inserter(to, is_even)); } 
+3
source

Here is my attempt.

 #include <algorithm> #include <iostream> #include <iterator> #include <vector> template <class Container, class Pred> class conditional_insert_iterator : public std::iterator< std::output_iterator_tag, void, void, void, void > { public: explicit conditional_insert_iterator(Container& c, Pred p) : container(&c), pred(p) {} conditional_insert_iterator& operator=(typename Container::const_reference value) { if (pred(value)) container->push_back(value); return *this; } conditional_insert_iterator& operator*() {return *this;} conditional_insert_iterator& operator++() {return *this;} conditional_insert_iterator& operator++(int) {return *this;} private: Container* container; Pred pred; }; template< class Container, class Pred> conditional_insert_iterator<Container, Pred> conditional_inserter( Container& c, Pred pred ) { return conditional_insert_iterator<Container, Pred>(c, pred); } using namespace std; int main() { vector<int> in = { 1, 2, 3, 4, 5, 6 }; vector<int> out; transform(in.begin(), in.end(), conditional_inserter(out, [](int i) { return i%2 == 0;}), [](int i) { return i + 2;}); for (auto i : out) cout << i << "\n"; return 0; } 
0
source

Source: https://habr.com/ru/post/958557/


All Articles