11

所以,在阅读了一些 SO 问题和答案之后,我仍然不明白为什么要使用

friend bool operator==( BaseClass const &left, BaseClass const &right )

代替

bool operator==( BaseClass const &right )

现在我有这样的东西http://pastebin.com/pKsTabC0 (固定) - 它似乎工作正常。但也许我错过了什么?有什么建议么?

更新 1

好的,我更改了源以使其正常工作http://ideone.com/fIAmB。删除了不必要的 virtual 并添加了 const。我还是不明白为什么要使用朋友...

4

4 回答 4

10

您的派生函数与父运算符没有相同的签名,因此它隐藏了父比较而不是覆盖它。这意味着您无论如何都不能使用虚拟性,因为左侧参数的静态类型决定了被调用的函数。

这就是为什么虚拟比较的常规方法是分配给基类中的虚拟比较函数的非成员相等运算符。

请考虑您对虚拟比较的特定需求,因为它可能是一种设计气味,您可以使用替代设计。

另请注意,成员比较(在这种情况下为相等)运算符通常应该是const.

编辑:您似乎只关心基于左侧参数的静态类型的比较,这应该是一个更容易的问题。在这种情况下,您的代码处理所有情况,除了左侧参数是隐式转换为 aBaseDerived通过继承以外的某种机制(转换运算符或转换构造函数)的类型。如果您不关心这些情况,那么成员平等就可以了。

最后一点,如果可以完全通过公共接口进行比较,我们(几乎)总是更喜欢非成员、非朋友函数,无论它是否是运算符。

EDIT2(非会员、非朋友的快速概览):

例如,假设您的类有一个公共key方法,并且如果两个实例的键相等,您希望调用对象相等。然后,不使用朋友或成员相等运算符,您可以独立编写您的相等:

bool operator==(const MyType& left, const MyType& right)
{
    return left.key() == right.key();
}
于 2012-07-24T14:34:30.060 回答
7

成员函数本身没有问题,但自由函数更通用。成员函数被迫让左侧操作数为类型BaseClass或子类型,而自由函数接受任何可隐式转换为 的BaseClass类型,而不仅仅是继承树中的类型。

于 2012-07-24T14:31:39.903 回答
4

我看到的最大问题virtual bool operator==( BaseClass const &right )是,如果没有多个动态调度*,以下断言将失败。

class Derived1 : public BaseClass {
    bool operator==( BaseClass const &right ) override {
        return true;
    }
}

class Derived2 : public BaseClass {
    bool operator==( BaseClass const &right ) override {
        return false;
    }
}

Derived1 d1;
Derived2 d2;
assert((d1 == d2) == (d2 == d1));

可以让朋友函数来处理这个问题,但老实说,在没有多次调度的情况下正确处理这样的事情会很麻烦。即使有多次分派,这种多态相等运算符的正确语义也可能不容易想出。


* 多重动态调度是对多个参数进行动态调度的能力,而不仅仅是 on this

于 2012-07-24T14:36:35.653 回答
2

您正在尝试提出两个独立的观点。首先,将运算符实现为成员函数而不是独立函数。其次,将其实现为虚拟功能(您甚至可以在标题中添加)。

你想通过“虚拟”部分说什么不清楚。在您的代码示例中,派生类运算符不会覆盖基类运算符,因为它们的签名不匹配。您的示例中没有多态性。所以,我不明白你所说的“工作正常”是什么意思。它根本不起作用(作为“虚拟”运算符),因为在您的代码中实际上没有任何东西可以“起作用”。目前您的代码只是编译,但它没有做任何事情。提供试图演示多态行为的代码——然后我们将看看它是否“工作”。

至于将二元运算符实现为成员函数……这个问题已经被多次讨论过了。作为成员函数实现的二元运算符在隐式参数转换方面表现“不对称”。这就是为什么将对称二元运算符实现为独立函数通常是一个更好的主意。

于 2012-07-24T14:39:38.967 回答