operator== 标记为 'override',但不会覆盖

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

我在覆盖基础 class 运算符== 时遇到问题。这是代码。

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

它抱怨

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

当我将函数转换为

时,编译器看起来很开心
bool operator==(const IClient<int8_t>& anotherClient) override {}

这是问题的解决方案还是我遗漏了什么?

谢谢

SeviceClient 和 IClient 是不同的类型。 所以,bool SeviceClient::operator==(const SeviceClient&) 没有基础声明。

你必须明白 C++ 有可能重载函数。 因此,要识别假设被调用的函数,不仅函数名称必须匹配,而且参数类型也必须匹配。当然有隐式转换可以调整参数以匹配函数参数类型,但请注意,如果有多个同名函数,则隐式转换会导致歧义。

如果您正在使用多态性,那么您必须记住子 class 应该可用,因为它只是父 class。持有指向 base class 指针的人应该不会注意到它实际上是一个 subclass.

所以当你重写虚函数时,参数的类型必须非常准确,所以一切都是明确的。

我怀疑您需要静态多态性和 CRTP 而不是动态多态性。所以像这样:

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

这个问题有两个方面。一、这个设计违反了C++的哪些规则?第二,为什么C++有这样的规则?

第一个很简单。您可以用具有完全相同参数列表的函数覆盖函数。由于 (const IClient& anotherClient)(const SeviceClient& anotherClient) 不完全相同,因此不会发生覆盖。结案。

第二个有点复杂。为了理解它,让我们使用更熟悉的 class 层次结构。有class Animal,有class Dog : public Animal,还有class Cat : public Animal

bool Animal::operator==(const Animal&) 告诉我们什么?它告诉我们可以将一种动物与另一种动物进行比较。特别是,我们可以比较狗和狗,猫和猫,狗和猫,老鼠和蝙蝠,鱼和蚊子,土豚和霸王龙。这是 class Animal 给你的承诺。如果我们有两个 Animal 对象,我们可以比较它们。

现在,覆盖此运算符的函数应该可以实现承诺。 bool Dog::operator==(const Dog&) 会那样做吗?不,它实现了一个更狭窄的承诺:这个 Animal(恰好是 Dog)可以与 Dog 进行比较。履行这个狭隘的承诺合法吗?绝对地。假装这个功能也实现了能够比较任何两种动物的广泛承诺是否合法?绝对不。所以我们可以声明这个函数,但不能声称它覆盖了 Animal 相等运算符。

如果有一条规则允许我们这样声明会怎样?

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

这个程序片段必须编译,因为每一行都是有效的(根据我们的幻想规则),但是它应该做什么?没有比较狗和猫的代码!

有些语言说“好的,没什么大不了的,我们只会抛出一个 运行 时间错误”。 C++ 不会那样做。如果您希望上面的代码抛出 运行 次错误,您应该自己编写代码来抛出 运行 次错误。这意味着用 bool Dog::operator==(const Animal&)bool Cat::operator==(const Animal&) 覆盖 bool Animal::operator==(const Animal&),并检查那里的错误情况。

虽然这可能不是一个好主意。对于 class 层次结构,平等通常毫无意义。通常最好完全省略比较,或者使其成为非虚拟的并仅在子 classes 中定义。