Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std algorithms with pointer to member as comparator/"key"

I often find myself using std::sort, std::max_element, and the like with a lambda that simply invokes a member function

std::vector<MyType> vec;
// populate...
auto m = std::max_element(std::begin(vec), std::end(vec),
    [](const MyType& a, const MyType& b) { return a.val() < b.val()})

this feels like a waste of characters and a loss of clarity. I'm aware that I could write another function/callable and pass a function pointer/callable object to these algorithm functions, but I often need to do this sort-by just once in a program and it doesn't strike me as a good way of addressing the problem. What I want to do, ideally is say:

auto m = std::max_element(std::begin(vec), std::end(vec), &MyType::val);

and have the objects be sorted by their val()s. Is there some part of the stdlib I'm overlooking that could assist me with this? or another simple way of doing it? I'd like to make what this is sorting or searching by as obvious as possible.

I'm aware that just &MyType::val isn't enough, I am looking for something that could perhaps wrap it, or provide a similar functionality without obscurring the meaning.

like image 509
Ryan Haining Avatar asked May 11 '14 19:05

Ryan Haining


2 Answers

You can use std::mem_fn (or std::tr1::mem_fn)

int main()
{
    std::vector<MyType> vec;

    auto m = std::max_element(std::begin(vec), std::end(vec), compare_by(std::mem_fn(&MyType::field)));
}

Of course, this assumes that you have a utility like compare_by in your toolbox (as you should :)):

template <typename F>
struct CompareBy {
    explicit CompareBy(F&& f) : f(std::forward<F>(f)) {}
    template <typename U, typename V> 
        bool  operator()(U const& u, V const& v) const {
            return f(u) < f(v);
        }

private:
    F f;
};

template <typename F>
CompareBy<F> compare_by(F&& f) { return CompareBy<F>(std::forward<F>(f)); }

See it Live On Coliru

like image 161
sehe Avatar answered Oct 23 '22 21:10

sehe


You can do it without introducing any new functions (templated or not).

Just use bind and std::less

auto m = std::max_element(vec.begin(), vec.end(), 
    bind(less<>(), bind(&MyType::val, _1), bind(&MyType::val, _2)));
like image 3
Nikos Athanasiou Avatar answered Oct 23 '22 19:10

Nikos Athanasiou