Thread: < overload, references and maps

  1. #1
    Registered User
    Join Date
    Jan 2005
    Posts
    108

    < overload, references and maps

    Hello all,

    Code:
    class KrakenDimension{
       public:
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          KrakenDimensionType type;
          int number;
          -- snip --
    }
    
    bool operator < (KrakenDimension a, KrakenDimension b){
       if(a.type < b.type)
          return true;
       else if (a.type > b.type)
          return false;
       else
          return a.number < b.number;
    }
    Now, it works fine and well (like you can do a < b) normally,
    but why does it stop working when I change the type signature of the comparison operator to:

    Code:
    bool operator < (KrakenDimension & a, KrakenDimension & b){
    ?

    Not that it doesn't stop working all of a sudden, but it stops working with this:

    Code:
    map<KrakenDimension, int> themap;
    just the declaration itself causes a compiler error (please brace for the long and cryptic message):

    Code:
    c:/dev-c++/include/c++/3.3.1/bits/stl_function.h: In member function `bool 
       std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = 
       KrakenDimension]':
    c:/dev-c++/include/c++/3.3.1/bits/stl_tree.h:1323:   instantiated from `std::_Rb_tree_iterator<_Val, _Val&, _Val*> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::lower_bound(const _Key&) [with _Key = KrakenDimension, _Val = std::pair<const KrakenDimension, int>, _KeyOfValue = std::_Select1st<std::pair<const KrakenDimension, int> >, _Compare = std::less<KrakenDimension>, _Alloc = std::allocator<std::pair<const KrakenDimension, int> >]'
    c:/dev-c++/include/c++/3.3.1/bits/stl_map.h:508:   instantiated from `typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::lower_bound(const _Key&) [with _Key = KrakenDimension, _Tp = int, _Compare = std::less<KrakenDimension>, _Alloc = std::allocator<std::pair<const KrakenDimension, int> >]'
    c:/dev-c++/include/c++/3.3.1/bits/stl_map.h:316:   instantiated from `_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = KrakenDimension, _Tp = int, _Compare = std::less<KrakenDimension>, _Alloc = std::allocator<std::pair<const KrakenDimension, int> >]'
    educt-dimension.cc:93:   instantiated from here
    c:/dev-c++/include/c++/3.3.1/bits/stl_function.h:197: error: no match for 
       'operator<' in '__x < __y'
    educt-dimension.cc:9: error: candidates are: bool operator<(KrakenDimension&, 
       KrakenDimension&)
    So yeah, any ideas anyone? Much thanks in advance

  2. #2
    carry on JaWiB's Avatar
    Join Date
    Feb 2003
    Location
    Seattle, WA
    Posts
    1,972
    Code:
    bool operator < (const KrakenDimension a, const KrakenDimension b){
       if(a.type < b.type)
          return true;
       else if (a.type > b.type)
          return false;
       else
          return a.number < b.number;
    }
    Is there any reason you aren't making it a member function?
    Code:
    class KrakenDimension
    {
    public:
      bool operator <(const KrakenDimension& rhs) const;
    private:
      int number; //data should be private
    };
    
    bool KrakenDimension::operator <(const KrakenDimension& rhs) const
    {
      return a.number<b.number;
    }
    "Think not but that I know these things; or think
    I know them not: not therefore am I short
    Of knowing what I ought."
    -John Milton, Paradise Regained (1671)

    "Work hard and it might happen."
    -XSquared

  3. #3
    Registered User
    Join Date
    Jan 2005
    Posts
    108
    well, that's what I had before, and I tried using const now:

    Code:
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //bool operator < (KrakenDimension a, KrakenDimension b){
    //   if(a.type < b.type)
    //      return true;
    //   else if (a.type > b.type)
    //      return false;
    //   else
    //      return a.number < b.number;
    //}
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    bool KrakenDimension::operator < (const KrakenDimension & b){
       if(type < b.type)
          return true;
       else if (type > b.type)
          return false;
       else
          return number < b.number;
    }
    and here's the whole class declaration:

    Code:
    ////////////////////////////////////////////////////////////////////////////////////////
    class KrakenDimension{
       public:
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          KrakenDimensionType type;
          int number;
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          KrakenDimension()
             : type(kdt_null),
               number(0)
               {}
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          KrakenDimension(KrakenDimensionType _type, int _number)
             : type(_type),
               number(_number)
               {}
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          bool operator == (KrakenDimension & b);
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          bool operator < (const KrakenDimension & b);
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          bool operator <= (KrakenDimension & b);
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          bool operator > (KrakenDimension & b);
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          bool operator >= (KrakenDimension & b);
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          bool operator != (KrakenDimension & b);
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    };
    now, again, using map I get this error message:
    Code:
    c:/dev-c++/include/c++/3.3.1/bits/stl_function.h: In member function `bool
       std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp =
       KrakenDimension]':
    c:/dev-c++/include/c++/3.3.1/bits/stl_tree.h:1323:   instantiated from `std::_Rb
    _tree_iterator<_Val, _Val&, _Val*> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compa
    re, _Alloc>::lower_bound(const _Key&) [with _Key = KrakenDimension, _Val = std::
    pair<const KrakenDimension, int>, _KeyOfValue = std::_Select1st<std::pair<const
    KrakenDimension, int> >, _Compare = std::less<KrakenDimension>, _Alloc = std::al
    locator<std::pair<const KrakenDimension, int> >]'
    c:/dev-c++/include/c++/3.3.1/bits/stl_map.h:508:   instantiated from `typename s
    td::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _
    Key, _Tp> >, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::
    lower_bound(const _Key&) [with _Key = KrakenDimension, _Tp = int, _Compare = std
    ::less<KrakenDimension>, _Alloc = std::allocator<std::pair<const KrakenDimension
    , int> >]'
    c:/dev-c++/include/c++/3.3.1/bits/stl_map.h:316:   instantiated from `_Tp& std::
    map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = KrakenDim
    ension, _Tp = int, _Compare = std::less<KrakenDimension>, _Alloc = std::allocato
    r<std::pair<const KrakenDimension, int> >]'
    educt-dimension.cc:93:   instantiated from here
    c:/dev-c++/include/c++/3.3.1/bits/stl_function.h:197: error: passing `const
       KrakenDimension' as `this' argument of `bool
       KrakenDimension::operator<(const KrakenDimension&)' discards qualifiers
    weird.. never came across something like this before, but then again I'm a c++ newbie

  4. #4
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,820
    How are you calling your map. I wrote some test code and this compiles for me.
    Code:
    #include <iostream>
    #include <map>
    #include <cstdlib>
    
    class Test
    {
        public:
            Test()
            {
                type = std::rand() % 10;
                number = std::rand() % 10;
            }
            bool operator <(const Test &cmp) const
            {
                if(type < cmp.type)
                {
                    return true;
                }
                if(type > cmp.type)
                {
                    return false;
                }
                return number < cmp.number;
            }
        private:
            int type;
            int number;
    };
    
    int main()
    {
        std::map<Test, int> theMap;
        
        return 0;
    }
    So I am thinking it is how you are using the map.
    Woop?

  5. #5
    Registered User
    Join Date
    Jan 2005
    Posts
    108
    Wow, that const at the end fixed it all. I don't really know why, but thanks a million for that

  6. #6
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    It isn't that big of a deal, but you should prefer to make it a non-member function like the first post (with the const reference of course).

    The reason the const fixes both situations is that the map compares constant versions of the data when adding it and so it cannot call functions that work on non-const data in case those functions change its const copies. The only way it can tell of the data will be changed is to look at the const declaration.

  7. #7
    carry on JaWiB's Avatar
    Join Date
    Feb 2003
    Location
    Seattle, WA
    Posts
    1,972
    >>It isn't that big of a deal, but you should prefer to make it a non-member function like the first post (with the const reference of course).

    Why is the non-member function preferred? Won't you also have to declare it as friend in that case?
    "Think not but that I know these things; or think
    I know them not: not therefore am I short
    Of knowing what I ought."
    -John Milton, Paradise Regained (1671)

    "Work hard and it might happen."
    -XSquared

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Why is the non-member function preferred?
    Suppose that objects of type B are implicitly convertible to objects of type A. Suppose we provide a B object to A::operator<(const A&) as the right hand operand. This B object would be implicitly converted to an A object, and all goes well. But if we provide it as the left hand operand, well, if B doesnt have an operator< and A is not implicitly convertible to B, then we have a problem. An operator<(const A&, const A&), on the other hand, would not face this problem at all.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed