Search code examples
c++c++11templatesoperator-overloadingenable-if

Adding overload operator == with enable_if


I have a working template class point with an overload for operator==.

Because of floating point comparison, I was trying to add a second overload with enable_if for floating point to use an almost equal function.

This is my attempt:

template<typename T>
class Point2D
{
public:
   Point2D(T x, T y);

   Point2D& operator= (const Point2D& point);
   bool     operator==(const Point2D& point) const;
   bool     operator!=(const Point2D& point) const;
};

template<typename T>
Point2D<T>::Point2D(T x, T y) : x_(x), y_(y)
{
}

template<typename T>
Point2D<T>& Point2D<T>::operator=(const Point2D& point)
{
   if(this != &point)
   {
      x_ = point.x_;
      y_ = point.y_;
   }

   return *this;
}

template<typename T>
bool Point2D<T>::operator==(const Point2D& point) const
{
   return (x_ == point.x_) && (y_ == point.y_);
}

template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
Point2D<T>::operator==(const Point2D& point) const
{
   return Traits::almost_equal(x_, point.x_) &&
          Traits::almost_equal(y_, point.y_);
}

Note:

  • This is a semplified example.

  • The code actually work without the enable_if overload

  • I have to separate declaration and implementation (both in .h), so please refer to the code as is.

The error the compiler gives me is

error: prototype for typename std::enable_if<std::is_floating_point<_Tp>::value, bool>::type Point2D<T>::operator==(const Point2D<T>&) const
  does not match any in class Point2D<T>
  Point2D<T>::operator==(const Point2D& point) const
  ^

error: candidate is: bool Point2D<T>::operator==(const Point2D<T>&)
const bool Point2D<T>::operator==(const Point2D& point) const
      ^

I don't understand what the error is referring to.


Solution

  • Really, just don't do that. Do this instead:

    template<typename T>
    class Point2D
    {
      bool equals(const Point2D& other, std::true_type is_floating_point ) const;
      bool equals(const Point2D& other, std::false_type is_floating_point ) const;
    public:
      Point2D(T x, T y);
    
      Point2D& operator= (const Point2D& point);
      bool     operator==(const Point2D& point) const;
      bool     operator!=(const Point2D& point) const;
    };
    

    now:

    template<typename T>
    bool Point2D<T>::operator==(const Point2D& point) const
    {
      return this->equals(point, std::is_floating_point<T>{});
    }
    template<class T>
    bool Point2D<T>::equals(const Point2D& point, std::false_type /*is_floating_point*/ ) const {
      return (x_ == point.x_) && (y_ == point.y_);
    }
    template<class T>
    bool Point2D<T>::equals(const Point2D& point, std::true_type /*is_floating_point*/ ) const {
      return Traits::almost_equal(x_, point.x_) &&
          Traits::almost_equal(y_, point.y_);
    }
    

    this is tag dispatching. It is cleaner and easier and takes less time to compile than SFINAE enable-if stuff.