3

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==

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

4

2 回答 2

6

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

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

使用 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'soperator==使其具有与 's 相同的签名Base

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

并且只要有可能,使用override这样就可以使编译器检测到这些类型的错误。

于 2013-11-13T02:03:44.450 回答
2

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

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

一种解决方案是使用double dispatch解决此问题。real_comparator您定义了一个实际执行比较工作的方法(或者可能是一个自由函数) 。有四个版本real_comparator,Base 和 Derived 类各有两个。

当我们这样做时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
  }
};

这应该是正确的,但也许可以在某些方面进行改进。任何反馈表示赞赏?

于 2013-11-13T11:19:58.370 回答