什么操作员应该被宣布为朋友?

时间:2011-06-06 17:35:45

标签: c++ operator-overloading

在一些书籍中,经常在互联网上,我看到“operator==应该被宣布为朋友”的建议。

如果必须将运算符声明为朋友以及何时应将其声明为成员,我应该如何理解?除了==<<之外,最常需要将哪些运营商声明为朋友?

4 个答案:

答案 0 :(得分:37)

这实际上取决于一个班级是否会在operator==(或其他运营商)的呼叫的左侧或右侧。如果一个类将位于表达式的右侧 - 并且不提供对可以与左侧进行比较的类型的隐式转换 - 则需要将operator==作为单独实现函数或作为类的friend。如果运营商需要访问私有类数据,则必须声明为friend

例如,

class Message {
    std::string content;
public:
    Message(const std::string& str);
    bool operator==(const std::string& rhs) const;
};

允许您将消息与字符串进行比较

Message message("Test");
std::string msg("Test");
if (message == msg) {
    // do stuff...
}

但不是相反

    if (msg == message) { // this won't compile

您需要在班级

中声明朋友operator==
class Message {
    std::string content;
public:
    Message(const std::string& str);
    bool operator==(const std::string& rhs) const;
    friend bool operator==(const std::string& lhs, const Message& rhs);
};

将隐式转换运算符声明为适当的类型

class Message {
    std::string content;
public:
    Message(const std::string& str);
    bool operator==(const std::string& rhs) const;
    operator std::string() const;
};

声明一个单独的函数,如果它不访问私有类数据则不需要成为朋友

bool operator==(const std::string& lhs, const Message& rhs);

答案 1 :(得分:19)

当你的操作符在类之外时,两个参数都可以参与隐式类型转换(而在类主体中定义运算符时,只有右侧操作数可以)。通常,这对所有经典二元运算符(即==!=+-<<,...)都有好处。

当然,如果您需要,您应该只声​​明您班级的操作员friend,而不是仅仅根据班级的公共成员计算他们的结果。

答案 2 :(得分:7)

通常,只有实现为真正需要访问其操作的类的私有或受保护数据的自由函数的运算符才应被声明为朋友,否则它们应该只是非朋友非成员函数。 / p>

通常,我作为成员函数实现的唯一运算符是那些基本上不对称且操作数不具有等效角色的运算符。我倾向于作为成员实现的那些是那些必须成为成员的人:简单的任务,()[]->以及复合赋值运算符,一元运算符以及{的一些重载对于本身属于流类或类类的类,{1}}和<<。我永远不会超载>>&&||

我倾向于将所有其他操作符实现为自由函数,最好使用它们所操作的类的公共接口,只在必要时才回退为朋友。

,!===<+等操作符保持为非成员函数,可以对左边和关于隐式转换序列的正确操作数有助于减少令人惊讶的不对称数量。

答案 3 :(得分:1)

没有人提到hidden friends idiom,我怀疑这就是书中的意思。

长版:https://www.justsoftwaresolutions.co.uk/cplusplus/hidden-friends.html

简短版本:

大多数情况下,运算符是通过 ADL(参数相关查找)找到的。当您不在命名空间 operator== 中时,这就是为 std::string 中的 std 定义的 std 被找到的方式。

操作员常见的问题之一是巨大的重载集。如果您尝试将 operator<< 用于不可打印的内容,您通常会在错误消息中看到这一点。

因此 - 如果您直接在包含该类的命名空间中声明 operator==,它将起作用,但它也会参与该命名空间中的所有重载解析,这会减慢您的编译速度并为您提供更多错误中的噪音。

介绍隐藏的朋友

struct X {
  friend bool operator==(const X& x, const X& y) {...}
};

如果操作数之一的类型为 operator==,则此 X 将仅被考虑用于重载解析。在所有其他情况下,它不会被看到,因此您的编译速度会更快,错误信息也会更好。

同样适用于所有两个操作数运算符,如 operator<< 以及其他用于 ADL 的函数,如 swap

我总是这样定义我的操作符,现在很多人认为这是一个很好的做法。

唯一的缺点是 - 没有很好的方法来定义它。您可能需要考虑分派给某个私有函数。 或者您可以这样做:https://godbolt.org/z/hMarb4 - 但这意味着至少在一个 cpp 文件中,operator== 将参与正常查找。