1

I was just curious to know how lower_bound and upper_bound functions in C++ standard library work using comparators. I could not understand how they actually work using the documentation given at cppreference.com. For ex.

vector<int> arr = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5};

auto it1 = lower_bound(arr.begin(), arr.end(), 3, less<int>());
auto it2 = lower_bound(arr.begin(), arr.end(), 3, less_equal<int>());
auto it3 = lower_bound(arr.begin(), arr.end(), 3, greater<int>());
auto it4 = lower_bound(arr.begin(), arr.end(), 3, greater_equal<int>());
auto it5 = lower_bound(arr.begin(), arr.end(), 3, equal_to<int>());
auto it6 = lower_bound(arr.begin(), arr.end(), 3, not_equal_to<int>());

// Output in comments
cout << it1 - arr.begin() << endl;      // 4
cout << it2 - arr.begin() << endl;      // 6
cout << it3 - arr.begin() << endl;      // 0
cout << it4 - arr.begin() << endl;      // 10
cout << it5 - arr.begin() << endl;      // 6
cout << it6 - arr.begin() << endl;      // 4

auto it7 = upper_bound(arr.begin(), arr.end(), 3, less<int>());
auto it8 = upper_bound(arr.begin(), arr.end(), 3, less_equal<int>());
auto it9 = upper_bound(arr.begin(), arr.end(), 3, greater<int>());
auto it10 = upper_bound(arr.begin(), arr.end(), 3, greater_equal<int>());
auto it11 = upper_bound(arr.begin(), arr.end(), 3, equal_to<int>());
auto it12 = upper_bound(arr.begin(), arr.end(), 3, not_equal_to<int>());

cout << it7 - arr.begin() << endl;      // 6
cout << it8 - arr.begin() << endl;      // 4
cout << it9 - arr.begin() << endl;      // 10
cout << it10 - arr.begin() << endl;     // 0
cout << it11 - arr.begin() << endl;     // 4
cout << it12 - arr.begin() << endl;     // 6

For lower_bound they use: (*it < val) [ (comp(*it, val)) for comparator]
For upper_bound they use: (!(val < *it)) [ (!comp(val, *it)) for comparator) ]

Can someone explain their working using comparators to get all the desired outputs?

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
Smile001
  • 147
  • 3
  • 9
  • You should read the documentation: https://en.cppreference.com/w/cpp/algorithm/lower_bound. **Many examples in your code has undefined behavior** because you don't follow the requirement of the functions and it is pointless to try to understand **undefined behavior**. Compiler might even assumes that a program does not have any undefined behavior and thus very strange things can happen if you lie to the compiler. – Phil1970 Dec 12 '20 at 20:37

1 Answers1

2

std::lower_bound requires that the input range be partitioned with respect to the comparator - that is, all elements for which comp(element, value) returns true must precede elements for which it returns false. In your example, only calls using less and less_equal satisfy this requirement; the other calls exhibit undefined behavior.

std::upper_bound requires that the input range be partitioned with respect to the expression !comp(value, element) - again, all elements for which it returns true must precede elements for which it returns false. In your example, only calls using less and less_equal satisfy this requirement; the other calls exhibit undefined behavior.

Calls involving less and less_equal behave as documented, and produce expected results.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • Thank you so much for this, I missed that partitioning concept, now I fully understand their working... – Smile001 Dec 13 '20 at 06:29