简体   繁体   中英

Using STL algorithms on unary functions

So I often want to perform some STL algorithm on a range of elements and instead of a comparison function, I would like to pass a unary function f.

For example I would like to write something like this

std::max_element(begin(range), end(range), f);

to find the maximum element in the range after applying f. My current workaround looks something like that:

std::max_element(begin(range), end(range, [&f](auto a, auto b){ return f(a) < f(b); });

At first glance, this may look like no problem. But f could be a lambda expression itself or in another way more complicate than just f. I have two problem with that piece of code: a) It is error prone because one could accidently write f(a) < f(a) (especially if more complicated and one used copy and past). This is the problem of code duplication b) It does not express the intent very well. If I want to sort by a function, I do not want to deal with a comparison.

Unfortunately I have not found a good solution to this kind of problem in the standard library (or even boost), so I would like to ask you what your solution to this problem is. Is there any reason for the non-existence of this overload in the algorithms?

Using c++ 20's ranges you can do:

std::ranges::max_element(range | std::views::transform(f));

One thing you can do is create your own generic comparator that accepts a unary function to perform a transform:

// Generic comparator
template<typename T, typename F>
struct transform_comparator_type
{
    transform_comparator_type(F f): f(f) {}
    bool operator()(T const& a, T const& b) const { return f(a) < f(b); }
    F f;
};

// Helper function to deduce the type of F
template<typename T, typename F>
auto transform_comparator(F f)
{
    return transform_comparator_type<T, F>(f);
}

int main()
{
    std::vector<int> v{1, 4, 3, 6, 0};

    auto e = std::max_element(std::begin(v), std::end(v),
        transform_comparator<int>([v](int i){ return 2 * i; }));

    // etc...
}

Edited to add:

The type can actually be deduced from the return type of the supplied transform function, so you don't need the helper function. You can do this instead:

template<typename F>
struct transform_comparator
{
    using T = decltype(F()({})); // deduce T from return type of F
    transform_comparator(F f): f(f) {}
    bool operator()(T const& a, T const& b) const { return f(a) < f(b); }
    F f;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM