16

I have a foo which is a std::vector<int>. It represents the "edge" values for a set of ranges.

For example, if foo is {1, 3, 5, 7, 11} then the ranges are 1-3, 3-5, 5-7, 7-11. Significantly for me, this equates to 4 periods. Note that each period includes the first number in a range, and not the last one. So in my example, 8 appears in the 3rd (zero-based) period. 7 also appears in the 3rd period. 11 and above doesn't appear anywhere. 2 appears in the 0th period.

Given a bar which is an int, I use

std::find_if(
    foo.begin(),
    foo.end(),
    std::bind2nd(std::greater<int>(), bar)
) - foo().begin() - 1;

to give me the period that should contain bar.

My problem: std::bind2nd is deprecated so I ought to refactor. What is the equivalent statement using updated functions? std::bind doesn't "drop in" in the obvious way.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
P45 Imminent
  • 8,319
  • 4
  • 35
  • 78
  • 3
    BTW, `std::distance(foo.begin(), std::lower_bound(foo.begin(), foo.end(), bar)) - 1;` seems more appropriate here. – Jarod42 Sep 23 '15 at 12:09
  • Was the `c++11` tag a requirement, or was it just a good fit when the question was asked? Anyway, if you're now actually fine with later versions, you could "tag it up" to allow @TemplateRex's answer to be fully impedance-matched. :) (See comments there.) – Sz. Aug 05 '19 at 21:41

3 Answers3

19

In C++11, you can use std::bind; it just isn't as obvious how to use it:

#include <functional>
using namespace std::placeholders;
std::find_if(
    foo.begin(),
    foo.end(),
    // create a unary function object that invokes greater<int>::operator()
    // with the single parameter passed as the first argument and `bar` 
    // passed as the second argument
    std::bind(std::greater<int>(), _1, bar)
) - foo().begin() - 1;

The key is the use of the placeholder argument, which are declared in the std::placeholders namespace. std::bind returns a function object that takes some number of parameters when it is invoked. The placeholders used inside the call to std::bind show how the arguments provided when the resulting object is invoked map to the argument list to the callable that you're binding to. So, for instance:

auto op1 = std::bind(std::greater<int>(), _1, bar);
op1(5); // equivalent to std::greater<int>()(5, bar)

auto op2 = std::bind(std::greater<int>(), bar, _1);
op2(5); // equivalent to std::greater<int>()(bar, 5)

auto op3 = std::bind(std::greater<int>(), _2, _1);
op3(5, bar); // equivalent to std::greater<int>()(bar, 5)

auto op4 = std::bind(std::greater<int>(), _1, _2);
op4(5, bar); // equivalent to std::greater<int>()(5, bar)
Jason R
  • 11,159
  • 6
  • 50
  • 81
16

What about going straight from Stone Age (bind2nd) to the Iron Age with a C++14 generic lambda, bypassing the Bronze Age (bind)?

std::find_if(foo.begin(), foo.end(), [&](auto const& elem) { 
    return elem > bar; 
}); 

And if the input is sorted

std::lower_bound(foo.begin(), foo.end(), bar); 

Lambdas read much easier and are also easier to inline than std::bind expresions. See e.g. Lavevej's CppCon 2015 talk.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • 2
    I agree that this is probably the easiest to read solution, but the question's tags only included C++11. – Jason R Sep 23 '15 at 12:55
  • 3
    @JasonR my philosophy is to show how I would code it, unless they claim that they really can't use C++14, and even then I'd show it if the savings are large enough :) The Q&A is not just for the OP, but for the public at large. But I upvoted your `bind` answer :) – TemplateRex Sep 23 '15 at 12:56
  • Fair enough; that makes sense. +1. – Jason R Sep 23 '15 at 13:00
  • @JasonR btw, Scott Meyers even had a talk a while ago called "burying bind" – TemplateRex Sep 23 '15 at 13:02
6

bind version would be:

bind(std::greater<int>(), placeholders::_1, bar)

but I think, it is more encouraged to use lambdas, as in:

[bar](const int a){return bar < a;}

It is also encouraged to use overloaded functions begin/end instead of method calls. so it would be like:

find_if(begin(foo), end(foo), [bar](const int a){return bar < a;})
behzad.nouri
  • 74,723
  • 18
  • 126
  • 124