Подтвердить что ты не робот

Какие операторы следует объявлять друзьями?

В некоторых книгах и часто в Интернете я вижу рекомендации типа "operator== должен быть объявлен как друг".

Как я должен понимать, когда оператор должен быть объявлен как друг и когда он должен быть объявлен как член? Каковы операторы, которые чаще всего должны быть объявлены друзьями помимо == и <<?

4b9b3361

Ответ 1

Это действительно зависит от того, будет ли класс находиться слева или справа от вызова operator== (или другого оператора). Если класс будет находиться в правой части выражения и не обеспечивает неявное преобразование в тип, который можно сравнить с левой стороной, вам нужно реализовать operator== как отдельную функцию или как a 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);

Ответ 2

Когда у вас есть ваши операторы вне класса, оба параметра могут участвовать в неявных преобразованиях типов (тогда как с операторами, определенными в теле класса, могут использоваться только правые операнды). Как правило, это преимущество для всех классических двоичных операторов (т.е. ==, !=, +, -, <<,...).

Конечно, вы должны только объявлять операторы friend вашего класса, если вам нужно, а не если они вычисляют их результат исключительно на основе публичных членов класса.

Ответ 3

Как правило, только операторы, которые реализуются как свободные функции, которые действительно нуждаются в доступе к закрытым или защищенным данным того класса, в котором они работают, должны быть объявлены друзьями, иначе они должны быть просто не-дружескими функциями, не являющимися членами.

Как правило, единственными операторами, которые я реализую как функции-члены, являются те, которые принципиально асимметричны и где операнды не имеют эквивалентных ролей. Те, которые я стараюсь реализовать в качестве членов, это те, которые должны быть членами: простое назначение, (), [] и -> вместе с составными операторами присваивания, унарные операторы и, возможно, некоторые перегрузки << и >> для классов, которые сами являются потоковыми или потоковыми классами. Я никогда не перегружаю &&, || или ,.

Все другие операторы, которые я использую как свободные функции, предпочитаю использовать открытый интерфейс классов, на которых они работают, возвращаясь к друзьям только там, где это необходимо.

Сохранение операторов, таких как !=, ==, <, +, / и т.д. как функции, не являющиеся членами, позволяет одинаково обрабатывать левый и правый операнды относительно неявных последовательностей преобразования, что помогает для уменьшения числа удивительных асимметрий.