operator== marked 'override', but does not override

Viewed 1443

I am have an issue while overriding the base class operator==. Here is the code.

#include <iostream>
#include <vector>

using namespace std;

template <typename T>
class IClient {
  virtual const std::vector<T>& getID() = 0;
  virtual bool isEqual(const std::vector<T>& anotherID) = 0;
  virtual bool operator==(const IClient& anotherClient) = 0;
};

class SeviceClient : public IClient<int8_t> {
  const std::vector<int8_t> ID;

 public:
  SeviceClient(const std::vector<int8_t>& ID) : ID(std::move(ID)) {}
  SeviceClient(const std::vector<int8_t>&& ID) : ID(std::move(ID)) {}

  const std::vector<int8_t>& getID() override {
      return ID;
  }
  bool isEqual(const std::vector<int8_t>& anotherID) override {
      return true;
  }
  bool operator==(const SeviceClient& anotherClient) override {
      return true;
  }
};

It complains that

 error: 'bool SeviceClient::operator==(const SeviceClient&)' marked 'override', but does not override

The compiler looks happy when i convert the function to

bool operator==(const IClient<int8_t>& anotherClient) override {}

Is this solution for the problem or am I missing something here ?

Thanks

3 Answers

SeviceClient and IClient are different types. So, bool SeviceClient::operator==(const SeviceClient&) hasn't base declaration.

You must understand that C++ has possibility to overload functions. So to identify function which suppose to be called not only function name must match, but also types of arguments. There is of course implicit conversion which can adapt arguments to match functions arguments types, but note that if there are multiple functions with same name then implicit conversion can lead to ambiguity.

If you are using polymorphism then you have to remember that child class should be usable as it was just parent class. Someone who holds pointer to base class should not notice that it is in fact a subclass.

So when you override virtual function types of arguments must much exactly, so everything is unambiguous.

I suspect that instead dynamic polymorphism you need static polymorphism and CRTP. So something like this:

template <typename Child, typename T>
class IClient {
  const std::vector<T>& getID() {
      return asChild()->getID();
  }

  bool isEqual(const std::vector<T>& anotherID) {
      return asChild()->isEqual(anotherID);
  }

  bool operator==(const Child& anotherClient) {
      return asChild()->isEqual(anotherClient);
  }

private:
  Child* asChild() {
      return static_cast<Child*>(this);
  }
  const Child* asChild() const {
      return static_cast<const Child*>(this);
  }

};

class SeviceClient : public IClient<SeviceClient, int8_t> {
  const std::vector<int8_t> ID;

public:
  SeviceClient(const std::vector<int8_t>& ID) : ID(std::move(ID)) {}
  SeviceClient(const std::vector<int8_t>&& ID) : ID(std::move(ID)) {}

  const std::vector<int8_t>& getID() {
      return ID;
  }
  bool isEqual(const std::vector<int8_t>& anotherID) {
      return true;
  }
  bool operator==(const SeviceClient& anotherClient) {
      return true;
  }
};

There are two sides to this question. One, what rules of C++ does this design violate? And two, why does C++ have such rules?

The first one is easy. You can override a function by a function with exactly the same argument list. Since (const IClient& anotherClient) is not exactly the same as (const SeviceClient& anotherClient), no override takes place. Case closed.

The second one is a bit more involved. To understand it, let's use a more familiar class hierarchy. There is class Animal, and there is class Dog : public Animal, and class Cat : public Animal.

What bool Animal::operator==(const Animal&) tells us? It tells us that we can compare an animal with another animal. In particular, we can compare a dog with a dog, and a cat with a cat, bit also a dog with a cat, and a rat with a bat, and a fish with a moskito, and an aardvark with a T. Rex. It is a promise that class Animal makes yo us. If we have two Animal object, we can compare them.

Now, a function that overrides this operator is supposed to fulfill the promise. Does bool Dog::operator==(const Dog&) does that? No, it fulfills a much narrower promise: that this Animal (that happens to be a Dog) can be compared with a Dog. Is it lawful to fulfill this narrow promise? Absolutely. Is it lawful to pretend that this function also fulfills the wide promise, that of being able to compare any two animals? Absolutely not. So we can declare this function, but cannot claim that it overrides the Animal equality operator.

What would happen if there was a rule that would allow us to claim that?

Dog d;
Cat c;
Animal& da = d;
Animal& ca = c;
std::cout << (da == ca);

This program fragment would have to compile, since every line is valid (under our fantasy rule), but what should it do? There is no code that compares a dog with a cat!

Some languages say "OK, no big deal, we'll just throw a run-time error". C++ doesn't do that. If you want the code above to throw a run-time error, you should write code that throws a run-time error yourself. This means overriding bool Animal::operator==(const Animal&) with bool Dog::operator==(const Animal&) and bool Cat::operator==(const Animal&), and checking for the error condition there.

It is probably not a good idea though. Equality usually makes little sense for class hierarchies. It is usually a much better idea to either omit the comparison altogether, or make it non-virtual and defined in child classes only.

Related