为什么在Base类而不是Derived类上调用virtual operator ==?

时间:2013-11-13 01:56:13

标签: c++ operator-keyword equality

我在virtual operator==课程中定义了Base。但由于某种原因,它似乎并没有真正被视为虚拟。

请参阅此示例代码:

#include <iostream>
#include <boost/unordered_set.hpp>

template<class T> struct EqualByValue {
  bool operator()(T const* a, T const* b) const { return *a == *b; }
};

struct Base {
  virtual bool operator==(Base const& other) const {
    std::cerr << "Base==" << std::endl;
  }
};

struct Derived : Base {
  virtual bool operator==(Derived const& other) const {
    std::cerr << "Derived==" << std::endl;
  }
};

int main(int argc, char** argv){
  typedef boost::unordered_set<Base*, boost::hash<Base*>, EqualByValue<Base> > 
    MySet;
  MySet s;
  Derived* d1 = new Derived();
  Derived* d2 = new Derived();
  s.insert(d1);
  s.insert(d2);
  s.find(d2);
  delete d1; delete d2; return 0;
}

输出为Base==,而不是所需的输出Derived==

为什么会这样,我该如何解决?

2 个答案:

答案 0 :(得分:6)

问题是你实际上没有覆盖operator==,因为原来的签名不同。让我们通过使用一个名为override的有用的C ++ 11特性来证明:

struct Derived : Base {
  virtual bool operator==(Derived const& other) const override {
  //                                                  ^^^^^^^^
    std::cerr << "Derived==" << std::endl;
  }
};

Compiling it with GCC会导致以下错误:

main.cpp:15:8: error: ‘bool Derived::operator==(const Derived&) const’ marked override, but does not override

   bool operator==(Derived const& other) const override {

要解决此问题,只需将Derive的{​​{1}}修改为与operator==具有相同的签名:

Base

尽可能使用struct Derived : Base { bool operator==(Base const& other) const override { std::cerr << "Derived==" << std::endl; // ... } }; ,这样您就可以让编译器检测到这些错误。

答案 1 :(得分:2)

我在最后添加了一个更简单的解决方案,如果两个对象的动态类型不同,则可以假设相等性应该为假。

Derived中的operator==不会覆盖Base中的operator==。有不同的签名,一个接受Base&amp;作为other,另一个是Derived&amp ;.它们必须相同才能覆盖工作(您可以稍微更改返回类型,但不能更改参数类型。)

一种解决方案是使用double dispatch解决此问题。您定义了一个名为real_comparator的方法(或者可能是一个自由函数),它将实际进行比较工作。 real_comparator有四个版本,每个版本都有两个版本。

当我们a==b时,我们希望将两个变量的动态类型考虑在内。编译器将a==b重写为a.operator==(b),因此,默认情况下,只有a参与多态。我们想要改变这一点,以便激活两个变量(以及所有四种可能性)。

诀窍在return other.real_comparator(*this);

struct Derived;

struct Base {
  virtual bool real_comparator(Base const& /*other*/) const {
          std::cerr << "Base == Base?" << std::endl;
          return false;
  }
  virtual bool real_comparator(Derived const& /*other*/) const {
          std::cerr << "Base == Derived?" << std::endl;
          return false;
  }

  virtual bool operator==(Base const& other) const {
    return other.real_comparator(*this);
  }
};

struct Derived : Base {
  virtual bool real_comparator(Base const& /*other*/) const override {
          std::cerr << "Derived == Base?" << std::endl;
          return false;
  }
  virtual bool real_comparator(Derived const& /*other*/) const override {
          std::cerr << "Derived == Derived?" << std::endl;
          return false;
  }

  virtual bool operator==(Base const& other) const override {
    return other.real_comparator(*this);
  }
};

我认为这段代码可以简化一些,特别是如果你有一条规则说“如果两个对象的动态类型不同,则比较的结果总是假的”,或类似的东西。


可以使用更简单的解决方案,但这取决于您要解决的问题。假设,如果两个对象具有不同的动态类型,则比较应该返回false:

#include<typeinfo>  // We need this for typeid to work
using namespace std;

struct Base {
  virtual bool operator==(Base const& other) const {
          if(typeid(other) != typeid(*this))
                  return false;
          else  
                cout << "A pair of Bases" << endl;
          return true; // replace this with code to compare two Base objects
  }
};

struct Derived : Base {

  virtual bool operator==(Base const& other) const override {
          if(typeid(other) != typeid(*this))
                  return false;
          else  
                cout << "A pair of Deriveds" << endl;

          // So 'other' is actually a Derived.
          const Derived * derived_pointer = dynamic_cast<const Derived*>(&other);
          // Now, we can compare 'this' to 'derived_pointer', both pointers to Derived
          return derived_pointer == this; // replace this with code to compare two Derived
  }
};

这应该是正确的,但也许可以在某些方面进行改进。有什么反馈意见吗?