为什么它不起作用
您的双重调度实现的问题是您希望equalityCheck()
调用最具体的。
但是您的实现完全基于多态基类,并且equalityCheck(const A*)
重载但不覆盖 equalityCheck(const Base*)
!
否则说,在编译时编译器知道A::equalityBounce()
可以调用equalityCheck(A*)
(因为this
是 an A*
),但不幸的是它调用的参数 Base::equalityCheck()
没有专门的版本。A*
如何实施?
为了使双重分派起作用,您需要在基类中具有双重分派的equalCheck() 的特定类型实现。
为此,Base 需要了解它的后代:
struct A;
struct Base {
virtual bool operator==(const Base& rhs) const
{
return rhs.equalityBounce(this);
}
virtual bool equalityBounce(const Base* lhs) const = 0;
virtual bool equalityCheck(const Base* lhs) const = 0;
virtual bool equalityCheck(const A* lhs) const = 0;
};
struct A : public Base {
...
bool equalityBounce(const Base* lhs) const override{
return lhs->equalityCheck(this);
}
bool equalityCheck(const Base* rhs) const override {
return false;
}
bool equalityCheck(const A* rhs) const override{
return a == rhs->a;
}
};
请注意使用override
以确保该函数确实覆盖了基的虚函数。
有了这个实现,它将起作用,因为:
A::equalityBounce()
将会通知Base::equalityCheck()
- 在这个函数的所有重载版本中,它会选择
Base::equalityCheck(A*)
因为this
是A*
- 被调用的
Base *lhs
对象将调用它的equalityCheck(A*)
. If lhs
is anA*
因此A::equalityCheck(A*)
它将产生预期的(正确)结果。恭喜!
- 假设
lhs
将是指向另一个类的指针,该类X
也派生自Base
. 在这种情况下,考虑到您将 an与 an进行比较,lhs->equalityCheck(A*)
将调用并且还可以返回正确的响应。 X::equalityCheck(A*)
X
A
如何使其可扩展?双发地图!
使用强类型语言的双重分派的问题在于,“反弹”对象需要知道如何与特定的(预先知道的)类进行比较。由于您的源对象和反弹对象具有相同的多态基类型,因此基需要知道所有涉及的类型。这种设计严重限制了可扩展性。
如果您希望能够在不事先知道基类的情况下添加任何派生类型,那么您必须通过动态类型(无论是 dynamic_cast 还是 typeid):
我在此向您提出动态可扩展性的建议。它使用单调度来比较相同类型的两个对象,并使用双调度映射来比较它们之间的不同类型(如果没有声明,则默认返回 false):
struct Base {
typedef bool(*fcmp)(const Base*, const Base*); // comparison function
static unordered_map < type_index, unordered_map < type_index, fcmp>> tcmp; // double dispatch map
virtual bool operator==(const Base& rhs) const
{
if (typeid(*this) == typeid(rhs)) { // if same type,
return equalityStrict(&rhs); // use a signle dispatch
}
else { // else use dispatch map.
auto i = tcmp.find(typeid(*this));
if (i == tcmp.end() )
return false; // if nothing specific was foreseen...
else {
auto j = i->second.find(typeid(rhs));
return j == i->second.end() ? false : (j->second)(this, &rhs);
}
}
}
virtual bool equalityStrict(const Base* rhs) const = 0; // for comparing two objects of the same type
};
然后 A 类将被重写为:
struct A : public Base {
A(int eh) : a(eh) {}
int a;
bool equalityStrict(const Base* rhs) const override { // how to compare for the same type
return (a == dynamic_cast<const A*>(rhs)->a);
}
};
使用此代码,您可以将任何对象与相同类型的对象进行比较。现在为了显示可扩展性,我创建了一个struct X
, 具有与 相同的成员A
。如果我想允许将 A 与 X 进行比较,我只需要定义一个比较函数:
bool iseq_X_A(const Base*x, const Base*a) {
return (dynamic_cast<const X*>(x)->a == dynamic_cast<const A*>(a)->a);
} // not a member function, but a friend.
然后为了使动态双调度工作,我必须将此功能添加到双调度地图:
Base::tcmp[typeid(X)][typeid(A)] = iseq_X_A;
然后结果很容易验证:
Base *w = new A(1), *x = new A(2), *y = new X(2);
std::cout << (*w == *w) << "\n"; // true returned by A::equalityStrict
std::cout << (*w == *x) << "\n"; // false returned by A::equalityStrict
std::cout << (*y == *x) << "\n"; // true returned by isseq_X_A