Q: Comparison operators in inherited classes I've defined an IntWrapper class like the following one:
struct IntWrapper
{
protected:
  int value;

public:
  explicit IntWrapper() = default;
  explicit IntWrapper(const int value) : value(value) {}

  bool operator< (const IntWrapper rhs) const { return value <  rhs.value; }
  bool operator> (const IntWrapper rhs) const { return value >  rhs.value; }
  bool operator<=(const IntWrapper rhs) const { return value <= rhs.value; }
  bool operator>=(const IntWrapper rhs) const { return value >= rhs.value; }
  bool operator==(const IntWrapper rhs) const { return value == rhs.value; }

  explicit operator int() const { return value; }
};

and the classes Foo and Bar that inherit from IntWrapper
struct Foo: IntWrapper
{
  using IntWrapper::IntWrapper;
};

struct Bar: IntWrapper
{
  using IntWrapper::IntWrapper;
};

I'd like to compare only objects of the same type. In other words I'd like the following piece give compilation error, instead of casting foo and bar to IntWrapper.
const Foo foo(1);
const Bar bar(2);
bool b = foo >= bar;

Since I have many other objects like Foo and Bar, in there any way to achieve my result keeping all the comparison operators inside IntWrapper?

A: You can add a dummy template to your IntWrapper to make the comparison operators work for same-type IntWrapper only:
template<class>
struct IntWrapper
{ /* same code */ };

struct Foo : IntWrapper<Foo> { using IntWrapper::IntWrapper; };
struct Bar : IntWrapper<Bar> { using IntWrapper::IntWrapper; };

int main()
{
    const Foo foo(1);
    const Bar bar(2);
    //bool b = foo >= bar; // error: no match for 'operator>=' (operand types are 'const Foo' and 'const Bar')
}

live demo

A: 
I'd like to compare only objects of the same type.

If you want two IntWrapper instances to be comparable as such, but not instances of classes which inherit from IntWrapper, you're practically saying you don't really want these classes to inherit from IntWrapper.


*

*@YSC suggested you prevent this inheritance using the CRTP pattern, so that each class inherits its "own" IntWrapper. That would prevent code duplication.

*You could alter the comparison operator so that it checks for the elements both being of the same subclass of IntWrapper.

*You could relegate the comparability to a subclass of IntWrapper, which Foo and Bar don't inherit.


And there are probably more ways to achieve this. But, again, it sounds like a questionable design.

A: Use templates for these operators and declare them as friends.
template <typename T, 
          std::enable_if_t<std::is_base_of_v<IntWrapper, T>, int> = 0> 
friend bool operator< (T lhs, T rhs) { return lhs.value <  rhs.value; }

Now if the real types of arguments are different, T cannot be deduced correctly.

Note I do not use const T because top-level const of function parameter is ignored.
