Операторы сравнения в унаследованных классах

Я определил класс IntWrapper следующим образом:

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; }
};

и классы Foo и Bar которые наследуются от IntWrapper

struct Foo: IntWrapper
{
  using IntWrapper::IntWrapper;
};

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

Я хотел бы сравнить только объекты одного типа. Другими словами, я хотел бы, чтобы следующий фрагмент дал ошибку компиляции вместо того, чтобы отбрасывать foo и bar в IntWrapper.

const Foo foo(1);
const Bar bar(2);
bool b = foo >= bar;

Поскольку у меня есть много других объектов, таких как Foo и Bar, каким-либо образом достичь моего результата, сохраняя все операторы сравнения внутри IntWrapper?

Ответ 1

Вы можете добавить фиктивный шаблон в свой IntWrapper чтобы заставить операторы сравнения работать только для одного типа IntWrapper:

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')
}

живая демонстрация

Ответ 2

Я хотел бы сравнить только объекты одного типа.

Если вы хотите, чтобы два экземпляра IntWrapper были сопоставимы как таковые, но не экземпляры классов, которые наследуются от IntWrapper, вы фактически говорите, что не хотите, чтобы эти классы наследовались от IntWrapper.

  • @YSC предложила вам предотвратить это наследование с использованием шаблона CRTP, чтобы каждый класс наследовал свой "собственный" IntWrapper. Это предотвратит дублирование кода.
  • Вы можете изменить оператор сравнения так, чтобы он проверял, чтобы элементы были одного и того же подкласса IntWrapper.
  • Вы можете отнести сопоставимость с подклассом IntWrapper, который Foo и Bar не наследуют.

И есть, вероятно, больше способов добиться этого. Но, опять же, это звучит как сомнительный дизайн.

Ответ 3

Используйте шаблоны для этих операторов и объявляйте их друзьями.

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; }

Теперь, если реальные типы аргументов различны, T не может быть выведено правильно.


Примечание. Я не использую const T потому что верхний уровень const параметра функции игнорируется.