2

I'm having some issues with the lower_bound comparison function.

I have a set of pairs ordered by the second value of the pair and i'm tryin to get the lower_bound from this set by a value.

My current code is:

#include <iostream> #include <algorithm> #include <vector> #include <set> using namespace std; struct setCompareFunctor { bool operator( )( const pair< int, int > &lhs, const pair< int, int > &rhs ) const { return( lhs.second <= rhs.second ); } }; struct setCompareFunctorAux { bool operator( )( const pair< int, int > &lhs, const pair< int, int > &rhs ) const { return( lhs.second <= rhs.second ); } bool operator( )( const pair< int, int > &lhs, int val ) const { return( lhs.second <= val ); } bool operator( )( int val, const pair< int, int > &rhs ) const { return( val <= rhs.second ); } }; int main( ) { set< pair< int, int >, setCompareFunctor > submultimi; submultimi.insert( make_pair( 1, 15 ) ); submultimi.insert( make_pair( 2, 9 ) ); submultimi.insert( make_pair( 3, 33 ) ); submultimi.insert( make_pair( 4, 44 ) ); submultimi.insert( make_pair( 5, 20 ) ); submultimi.insert( make_pair( 6, 15 ) ); set< pair< int, int >, setCompareFunctor >::iterator it = lower_bound( submultimi.begin( ), submultimi.end( ), 20, setCompareFunctorAux( ) ); cout << ( *it ).second << endl; return 0; } 

The expected result is 15, but the real result is 33.

What is wrong ?

4
  • Here's something to play with: coliru.stacked-crooked.com/a/710c3bbde16a5ecd Commented Oct 21, 2017 at 13:11
  • i'm compiling it under macosx xcode and is working.. hmm.. Commented Oct 21, 2017 at 13:12
  • 2
    setCompareFunctor is not a valid comparator as per the strict weak ordering criteria. The <= condition should be <. But that aside, how do you want your elements sorted? Why is the expected result 15? Commented Oct 21, 2017 at 13:12
  • not working.. i've tried.. it always works like an upper_bound, not a lower one Commented Oct 21, 2017 at 13:13

4 Answers 4

5

The expected result is 15, but the real result is 33.

No, the expected result is 20, since the function "Returns an iterator pointing to the first element in the range [first,last) which does not compare less than val.", as you can read in the std::lower_bound reference.

You will not get this result, because you use <= instead of < in your setCompareFunctorAux struct.

As a result, when you search for 20, it get confused from the equality, and go towards the wrong direction when searching.


PS: Not related to your problem, but setCompareFunctor is not a valid comparator, because it doesn't satisfy the strict weak ordering. In order to do so, just change <= to <. Read more in Operator< and strict weak ordering.

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

5 Comments

ok .. this is a good answer. but, there is a way to get '15' ?
@AdiPîslaru thanks. And you asked a good question. =) Yes, pass 15, instead of 20 as the function's argument.
haha :)) can't do this.. i used that value just for an example, the code will be implemented in an algorithm .. i want to have something like a lower bound, but not equal with the value passed..
@AdiPîslaru: But that is upper_bound. Maybe you mean “greatest value less than a query”, which is just the element before lower_bound, if any exists.
Your recommended iterator type doesn’t match the container.
2

The ordering for a set must act like <, not <=. Since you have an element with the key you’re looking for, the <= is wrong and sends the search the wrong way.

Meanwhile, using std::lower_bound on a set is wasteful: the iterators don’t expose the search structure, so the search is effectively linear. You can avoid this with C++14’s heterogeneous comparisons if you define setCompareFunctorAux ::is_transparent.

1 Comment

If i put <, it works like an upper_bound, don't know why
1

You have to use the strict less operator From the C++ 2017 Standard (28.7 Sorting and related operations)

3 For all algorithms that take Compare, there is a version that uses operator< instead. That is, comp(*i, *j) != false defaults to *i < *j != false. For algorithms other than those described in 28.7.3, comp shall induce a strict weak ordering on the values.

4 The term strict refers to the requirement of an irreflexive relation (!comp(x, x) for all x), and the term weak to requirements that are not as strong as those for a total ordering, but stronger than those for a partial ordering...

struct setCompareFunctor { bool operator( )(const pair< int, int > &lhs, const pair< int, int > &rhs) const { return(lhs.second < rhs.second); } }; struct setCompareFunctorAux { bool operator( )(const pair< int, int > &lhs, const pair< int, int > &rhs) const { return(lhs.second < rhs.second); } bool operator( )(const pair< int, int > &lhs, int val) const { return(lhs.second < val); } bool operator( )(int val, const pair< int, int > &rhs) const { return(val < rhs.second); } }; 

Take into account that within the called algorithm there is used the operator

struct setCompareFunctorAux { //... bool operator( )(const pair< int, int > &lhs, int val) const { return(lhs.second < val); } }; 

Here is a demonstrative program

#include <iostream> #include <set> #include <algorithm> int main() { using namespace std; struct setCompareFunctor { bool operator( )(const pair< int, int > &lhs, const pair< int, int > &rhs) const { return(lhs.second < rhs.second); } }; struct setCompareFunctorAux { bool operator( )(const pair< int, int > &lhs, const pair< int, int > &rhs) const { return(lhs.second < rhs.second); } bool operator( )(const pair< int, int > &lhs, int val) const { return(lhs.second < val); } bool operator( )(int val, const pair< int, int > &rhs) const { return(val <= rhs.second); } }; set< pair< int, int >, setCompareFunctor > submultimi; submultimi.insert(make_pair(1, 15)); submultimi.insert(make_pair(2, 9)); submultimi.insert(make_pair(3, 33)); submultimi.insert(make_pair(4, 44)); submultimi.insert(make_pair(5, 20)); submultimi.insert(make_pair(6, 15)); for (const auto &p : submultimi) { cout << "{ " << p.first << ", " << p.second << " } "; } cout << endl; set< pair< int, int >, setCompareFunctor >::iterator it = lower_bound(submultimi.begin(), submultimi.end(), 20, setCompareFunctorAux()); cout << (*it).second << endl; return 0; } 

Its output is

{ 2, 9 } { 1, 15 } { 5, 20 } { 3, 33 } { 4, 44 } 20 

The expected result is 15, but the real result is 33.

And the expected and correct result is 20 because there is an element with the second value equal to 20 and you are searching exactly 20.

Take into account that the template class std::set has its own member function lower_bound.

4 Comments

ok but if i use < instead <=, the duplicate values are not inserted in the set, for e.g. only one 15 value will be in the set and there are 2 inserted.
@AdiPîslaru If you want to use duplicate values then use the standard container std::multiset.
If i use 10 instead 20 in : set< pair< int, int >, setCompareFunctor >::iterator it = lower_bound(submultimi.begin(), submultimi.end(), 10, setCompareFunctorAux()); , the result is wrong, the result is 15 but the lower bound is 9
@AdiPîslaru lower_bound returns an iterator before the position where a new element with the value could be inserted. It can nor return 9 because 9 is less than 10.
0

I think you are trying to achieve something different from what std::lower_bound can give to you.

#include <algorithm> #include <iostream> #include <set> #include <utility> using my_key = std::pair<int, int>; int main(int argc, char *argv[]) { auto comp = [](const my_key &l, const my_key &r) { return l.second < r.second; }; std::set<my_key, decltype(comp)> submultimi(comp); submultimi.insert({1, 15}); submultimi.insert({2, 9}); submultimi.insert({3, 33}); submultimi.insert({4, 44}); submultimi.insert({5, 20}); submultimi.insert({6, 15}); // "<=" inside comp will put this pair into the set for (const auto &elem : submultimi) std::cout << elem.first << " -> " << elem.second << '\n'; auto it = std::lower_bound( submultimi.begin(), submultimi.end(), 20, [](const my_key &l, const int &r) { return l.second < r; }); std::cout << it->second << '\n'; return 0; } 

outputs

2 -> 9 1 -> 15 # note the lack of 6 -> 15 in the set 5 -> 20 3 -> 33 4 -> 44 20 

Everything seems legitimate as per the documentation provided in http://en.cppreference.com/w/.

I assume you are trying to put 6->15 using some trick, and the very same trick breaks std::lower_bound due to the violation of the weak ordering as is already pointed out by the other responders.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.