7

Say I have class A with member function int f() { return m_; } where m_ is a data member. I have a vector<A> called vec, and I want to find the object with maximum m_ value in vec using std::max_element. I can do it with a lambda:

auto cmp = [] (A& x, A& y) { return x.f() < y.f(); };
A& max_elem = *std::max_element(vec.begin(), vec.end(), cmp);

Is there some bind (or some such) trickery that will allow me to do it without a lambda and without defining my own comparator from scratch? Please don't suggest better ways to solve the max finding problem. It's only an example. The general question is how to wrap a function object that takes more than one argument (e.g., std::less<int>, which I would use as the comparator in the above example) in such a way that that arguments sent to it go through some other function (or member function) before getting to it.

7
  • Since you're arbitrarily selective on your decision of which f() shall be your comparator base, I see little hope of doing this without a custom comparator of some kind (be it functor or lambda, and my money is on the former for pointer-to-member convenience). Or... I didn't understand the problem at all (its 2:30am here, really should be in bed right now). Commented Feb 19, 2014 at 10:38
  • Look into std::bind, std::placeholders::_1 and related. Commented Feb 19, 2014 at 10:38
  • When I said "without defining my own comparator from scratch" I meant that I want to put something together from things like less<> and other building blocks (such as bind), that would ultimately call f(). I already looked into bind and placeholders but couldn't figure out how to do it with those. Commented Feb 19, 2014 at 10:44
  • 1
    @WhozCraig: There's a well-known alternative which doesn't need a custom comparator: a transform iterator which returns wrappedIter->f() from its operator*. Commented Feb 19, 2014 at 11:00
  • I hope you realize that using bind will be slower. Commented Feb 19, 2014 at 11:34

2 Answers 2

12

Yes, there is:

using namespace std::placeholders;
A& max = *std::max_element(begin(vec), end(vec)
    , std::bind(
        &std::less<int>::operator()
        , std::less<int>()
        , std::bind(&A::f, _1)
        , std::bind(&A::f, _2)
    )
);

Live example

However, if I ever saw this in code review, I'd immediately say "turn that ugliness into a lambda."


Thanks to @n.m. for poining out that the code can be shortened a bit:

using namespace std::placeholders;
A& max = *std::max_element(begin(vec), end(vec)
    , std::bind(
        std::less<int>()
        , std::bind(&A::f, _1)
        , std::bind(&A::f, _2)
    )
);

Live example

I still consider a lambda more readable, though. Bear in mind that you can use a lambda directly in the call to max_element() (no need for the variable cmp in your original code).

Sign up to request clarification or add additional context in comments.

5 Comments

Ok. that's just awesome.
operator() is called operator() for a reason: you don't have to name it when you want to call it ;) std::bind(std::less<int>(), std::bind(&A::f, _1), std::bind(&A::f, _2)) is enough.
Interesting. This was exactly my first thought, but I was so sure the _1 and _2 wouldn't pass through (not knowing how all of this is implemented), that I didn't even try it. Lemme see if it works.
Yup. It works. So now I'll dig into placeholders to understand how this magic happens. Thanks!
@Ari The outermost functor is a std::bind, which knows how to interpret placeholders and nested bind expressions. That's all that is needed for it to work.
0

There's a number of ways you can supply a predicate to a algorithm. Below you'll see a number of examples of how this can be done. Here's a summary of the calls to std::max_element():

//here we're using an in-place lambda
A& max_elem = *std::max_element(vec.begin(), vec.end(), [](A& x, A&y){ return x.f() < y.f(); });
// here we're binding to a non-static member function
A& max_elem = *std::max_element(vec.begin(), vec.end(), std::bind(&A::compare, A(0), std::placeholders::_1, std::placeholders::_2));
// here we're binding to a non-member function
A& max_elem = *std::max_element(vec.begin(), vec.end(), std::bind(gcompare, std::placeholders::_1, std::placeholders::_2));
// here we're using the non-member function without using bind
A& max_elem = *std::max_element(vec.begin(), vec.end(), gcompare);

We could also use std::function.

Here's the full test source code so that you can play with it yourself:

class A
{
    int m_ = 0;
public:
    A(int max) : m_(max)
    {

    }
    A(const A& that)
    {
        m_ = that.m_;
    }
    A& operator=(const A& that)
    {
        m_ = that.m_;
    }
    int f() const
    {
        return m_;
    }
    bool compare(const A&x, const A&y) const
    {
        return x.m_ < y.m_;
    }
    static bool scompare(const A&x, const A&y)
    {
        return x.m_ < y.m_;
    }
};

static bool gcompare(const A&x, const A&y)
{
    return x.f() < y.f();
}

int main()
{
    std::vector<A> vec;
    vec.emplace_back(5);
    vec.emplace_back(7);
    vec.emplace_back(4);
    vec.emplace_back(9);
    vec.emplace_back(12);
    vec.emplace_back(1);


    A& max_elem = *std::max_element(vec.begin(), vec.end(), [](A& x, A&y){ return x.f() < y.f(); });
    A& max_elem = *std::max_element(vec.begin(), vec.end(), std::bind(&A::compare, A(0), std::placeholders::_1, std::placeholders::_2));
    A& max_elem = *std::max_element(vec.begin(), vec.end(), std::bind(gcompare, std::placeholders::_1, std::placeholders::_2));
    A& max_elem = *std::max_element(vec.begin(), vec.end(), gcompare);

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.