Chapter 12. How to Write Consistent Comparison Operators

If you wrote a new class MyClass, you might want sometimes to write expressions like this:

MyClass x, y;
  /// some code initializing x and y
  if(x < y) {
    // do something
  } else if (x == y) {
    // do something else
  }

Even if you don’t need comparison operators (<, <=, etc.) yourself, you might find that someone attempts to use your class with Standard Template Library operations that require you to define these operators. For example, if you try to sort a vector of instances of your class:

vector<MyClass> v;
v.push_back(MyClass(3));
v.push_back(MyClass(1));
v.push_back(MyClass(2));

sort(v.begin(), v.end());

an attempt to compile this code fills the screen with diagnostics that look like this:

/usr/include/c++/4.2.1/bits/stl_heap.h:121: error: no
      match for 'operator<' in '__first.
      __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator+
      [with _Iterator = MyClass*, _Container = std::vector<MyClass,
      std::allocator<MyClass> >](((const ptrdiff_t&)((const
      ptrdiff_t*)(&
      __parent)))).__gnu_cxx::__normal_iterator<_Iterator,
      _Container>::operator* [with _Iterator = MyClass*, _Container =
      std::vector<MyClass, std::allocator<MyClass> >]() <
      __value'

Although this output is not easily readable by a human, after some effort one can find in that pile of information the following useful piece: no match for ‘operator<’. What the compiler is unhappy about is that the class MyClass does not define a < operator. All you have to do is add to the definition of MyClass:

class MyClass {
 public:
  // constructors, etc…
  bool operator < (const MyClass& that) const {
    // some code returning bool
    return my_data_ < that.my_data_;
  }

 private:
  Int my_data_;

and the example compiles, runs, and sorts the vector. The same thing happens if you try to use your class in std::set<MyClass> or as a key in std::map<MyClass, AnyOtherClass>. While STL is relatively undemanding and in most cases will be satisfied by the definition of only one < operator, there might be cases when you want to define several comparison operators or potentially all of them. For example, suppose you’ve decided to write a Date class that would encapsulate the calendar date and you expect that other programmers might want to use all kinds of comparisons: date1 >= date2, etc. There are six comparison operators:

<
>
<=
>=
==
!=

From the point of view of C++, these operators could be written as six totally independent functions, and nothing in C++ prevents you from writing each one any way you like. However, the user of your class MyClass would expect that if instances of this class satisfy the inequality x1 < x2, then it must also be true that x1 <= x2 and that x2 > x1. In other words, there are some logical relations between these operators, and after writing each comparison operator, it would be a good idea to make sure that these relations hold in order to avoid confusion. In fact, no additional work to achieve this is necessary. There is an easy way to kill all six birds with one stone in two steps.

  1. In your class, define the following method:

    class MyClass {
     public:
      // some code…
    
      // Returns negative int  when *this <  that,
      //         0             when *this == that and
      //         positive int  when *this >  that.
      int CompareTo(const MyClass& that) const;
  2. Define all six comparison operators by using the following macro inside the public section of your class:

    SCPP_DEFINE_COMPARISON_OPERATORS(MyClass)

I have defined SCPP_DEFINE_COMPARISON_OPERATORS in the file scpp_types.hpp as follows:

#define SCPP_DEFINE_COMPARISON_OPERATORS(Class)                             
  bool operator < (const Class& that) const { return CompareTo(that) < 0; } 
  bool operator > (const Class& that) const { return CompareTo(that) > 0; } 
  bool operator ==(const Class& that) const { return CompareTo(that) ==0; } 
  bool operator <=(const Class& that) const { return CompareTo(that) <=0; } 
  bool operator >=(const Class& that) const { return CompareTo(that) >=0; } 
  bool operator !=(const Class& that) const { return CompareTo(that) !=0; }

In one long line, this macro defines all six comparison operators for you in a consistent way. In order for this to work, the only thing you need to do is provide the CompareTo() function in your class. If you ever decide to change the definition of what you mean by > or <= for the instances of your class, you can simply edit that function and the rest will behave accordingly while preserving all the relations one would expect between different comparison operators.

Rule for this chapter to avoid errors when writing comparison operators:

  • Write a CompareTo() function and use the SCPP_DEFINE_COMPARISON_OPERATORS macro to implement all the comparison operators.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.245.140