My question: Is there a way to create an iterator over all even values that can be used to find the minimum element?
Yes!
It's slightly unfortunate that you're limited to C++17 with no Boost, because you ideally want ranges - specifically ranges::filter_view etc. which was added in C++20, and preceded by the Boost.Range library.
You may possibly be able to use the intermediate experimental range extension.
If none of those are viable, you can of course write your own filtered_iterator to use with std::min_element.
It's not much fun: although it's probably more reusable (and easier to test) than encoding all the logic into a single lambda, it's a lot of work if you're not planning to reuse it. Also, C++ iterators aren't ideally suited to emulating a Python-style generator, as demonstrated by the redundant end iterator e_ and the copy-assignment operator. You can't elide the end & predicate members of the filtered end iterator either, because both iterators usually need to be the same type.
template <typename BaseIterator, typename UnaryPredicate>
class filter_iterator
{
BaseIterator i_;
BaseIterator e_;
UnaryPredicate pred_;
public:
using reference = typename std::iterator_traits<BaseIterator>::reference;
using value_type = typename std::iterator_traits<BaseIterator>::value_type;
filter_iterator(filter_iterator &&) = default;
filter_iterator(filter_iterator const&) = default;
filter_iterator(BaseIterator i, BaseIterator e, UnaryPredicate p)
: i_(i), e_(e), pred_(p)
{}
filter_iterator& operator=(filter_iterator &&) = default;
filter_iterator& operator=(filter_iterator const& other) {
i_ = other.i_;
e_ = other.e_;
// This is questionable, because we can't copy the predicate without adding
// a level of indirection (ie, always wrapping it in std::function).
// For now, just assume it is stateless for convenience.
return *this;
}
bool operator==(filter_iterator const& other) const
{
return i_ == other.i_;
}
filter_iterator& operator++() {
// We could check i_ is not already e_ here,
// but the caller is required to check this outside anyway
i_ = find_if(next(i_), e_, pred_);
return *this;
}
filter_iterator operator++(int) const {
filter_iterator i(*this);
++i;
return i;
}
reference operator*() { return *i_; }
std::add_const_t<reference> operator*() const { return *i_; }
};
template <typename BaseIterator, typename UnaryPredicate>
bool operator!=(filter_iterator<BaseIterator, UnaryPredicate> const& a,
filter_iterator<BaseIterator, UnaryPredicate> const& b)
{
return !(a == b);
}
Then the wrapper function hides most of this ugliness for us:
template <typename BaseIterator, typename UnaryPredicate>
std::pair<filter_iterator<BaseIterator, UnaryPredicate>,
filter_iterator<BaseIterator, UnaryPredicate>>
filter(BaseIterator b, BaseIterator e, UnaryPredicate p)
{
using f = filter_iterator<BaseIterator, UnaryPredicate>;
auto fbegin = find_if(b, e, p);
return {f{fbegin, e, p}, {e, e, p}};
}
and we can use it like:
int main() {
std::vector<int> a {7, 1, 8, 4, 3, 2};
auto be = filter(a.begin(), a.end(),
[](int i){ return (i%2) == 0;});
auto min = std::min_element(be.first, be.second);
return *min;
}