Here is a quick relatively minimal filter function.
It takes a predicate. It returns a function object that takes an iterable.
It returns an iterable that can be used in a for(:) loop.
template<class It> struct range_t { It b, e; It begin() const { return b; } It end() const { return e; } bool empty() const { return begin()==end(); } }; template<class It> range_t<It> range( It b, It e ) { return {std::move(b), std::move(e)}; } template<class It, class F> struct filter_helper:range_t<It> { F f; void advance() { while(true) { (range_t<It>&)*this = range( std::next(this->begin()), this->end() ); if (this->empty()) return; if (f(*this->begin())) return; } } filter_helper(range_t<It> r, F fin): range_t<It>(r), f(std::move(fin)) { while(true) { if (this->empty()) return; if (f(*this->begin())) return; (range_t<It>&)*this = range( std::next(this->begin()), this->end() ); } } }; template<class It, class F> struct filter_psuedo_iterator { using iterator_category=std::input_iterator_tag; filter_helper<It, F>* helper = nullptr; bool m_is_end = true; bool is_end() const { return m_is_end || !helper || helper->empty(); } void operator++() { helper->advance(); } typename std::iterator_traits<It>::reference operator*() const { return *(helper->begin()); } It base() const { if (!helper) return {}; if (is_end()) return helper->end(); return helper->begin(); } friend bool operator==(filter_psuedo_iterator const& lhs, filter_psuedo_iterator const& rhs) { if (lhs.is_end() && rhs.is_end()) return true; if (lhs.is_end() || rhs.is_end()) return false; return lhs.helper->begin() == rhs.helper->begin(); } friend bool operator!=(filter_psuedo_iterator const& lhs, filter_psuedo_iterator const& rhs) { return !(lhs==rhs); } }; template<class It, class F> struct filter_range: private filter_helper<It, F>, range_t<filter_psuedo_iterator<It, F>> { using helper=filter_helper<It, F>; using range=range_t<filter_psuedo_iterator<It, F>>; using range::begin; using range::end; using range::empty; filter_range( range_t<It> r, F f ): helper{{r}, std::forward<F>(f)}, range{ {this, false}, {this, true} } {} }; template<class F> auto filter( F&& f ) { return [f=std::forward<F>(f)](auto&& r) { using std::begin; using std::end; using iterator = decltype(begin(r)); return filter_range<iterator, std::decay_t<decltype(f)>>{ range(begin(r), end(r)), f }; }; };
I took short cuts. A real library should make real iterators, not the for(:)-qualifying pseudo-fascades I did.
At point of use, it looks like this:
int main() { std::vector<int> test = {1,2,3,4,5}; for( auto i: filter([](auto x){return x%2;})( test ) ) std::cout << i << '\n'; }
which is pretty nice, and prints
1 3 5
Live example.
There is a proposed addition to C++ called Rangesv3 which does this kind of thing and more. boost also has filter ranges/iterators available. boost also has helpers that make writing the above much shorter.
std::copy_if, but the selections aren't lazyifinside aforyou mention is not only pretty much functionally equivalent to the other examples but would also probably be faster in a lot of cases. Also for someone who claims to like functional style, what you're promoting seems to go against functional programming's dearly beloved concept of purity sinceDoStuffclearly has side effcets.