Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

upper_bound and lower_bound inconsistent value requirements

I saw what looks like an inconsistency in the std::lower_bound() and std::upper_bound() syntaxes (well, type-conversion, really) and was wondering if anyone could elucidate please? Per the comments, line 2 will not compile despite its obvious similarity to line 1; you need to use the form shown on line 3 (on gcc 4.7.3 / ubuntu 64-bit at least - that's all I've got to play with)

#include <set>
#include <algorithm>

using namespace std;

class MyInt {
  private:
    int val;
  public:
    MyInt(int _val): val(_val) {}
    bool operator<(const MyInt& other) const {return val < other.val;}
};

int main() {
    set<MyInt> s;
    s.insert(1);  // demonstrate implicit conversion works
    s.insert(MyInt(2));
    s.insert(3); // one last one for the road
    set<MyInt>::iterator itL = lower_bound(s.begin(), s.end(), 2); //LINE 1
    // the line below will NOT compile
    set<MyInt>::iterator itU = upper_bound(s.begin(), s.end(), 2); //LINE 2
    // the line below WILL compile
    set<MyInt>::iterator itU2 = upper_bound(s.begin(), s.end(), MyInt(2)); // LINE 3
    return 0;
}
like image 336
aho Avatar asked Nov 09 '15 03:11

aho


1 Answers

I don't think it's a bug. If you look at the (possible) implementation of std::upper_bound, the comparison is done like

if (!(value < *it)) { ... } // upper_bound, implicit conversion `MyInt`->`int` doesn't work

And because operator< is a member function of MyInt (and not of int, which is not a class type), the code doesn't compile, since there is no conversion from MyInt to int. On the other hand, in std::lower_bound, *it appears on the lhs of the comparison, and value (of type int) can be implicitly converted to MyInt when passed to MyInt::operator<.

if (*it < value) { ... } // lower_bound, implicit conversion `int`->`MyInt` works

This is the reason why it's better to implement comparison operators as non-members, so you don't have this asymmetry. This is also mentioned in Scott Meyers' Effective C++ book: Item 24: Declare non-member functions when type conversions should apply to all parameters.

Quick and dirty fix: define an MyInt::operator int(){return val;} for implicit conversion MyInt to int. (EDIT: doesn't really work, ambiguity). What works is removing the need for implicit conversion

set<MyInt>::iterator itU = upper_bound(s.begin(), s.end(), MyInt(2));

instead.

like image 63
vsoftco Avatar answered Sep 18 '22 17:09

vsoftco