При перегрузке в качестве функции-члена a << b
интерпретируется как a.operator<<(b)
, поэтому для него требуется только один явный параметр (с this
в качестве скрытого параметра).
Поскольку для этого требуется, чтобы перегрузка была частью класса, используемого в качестве левого операнда, это бесполезно с обычными ostream
с и так далее. Это потребует, чтобы ваша перегрузка была частью класса ostream
, а не частью вашего класса. Поскольку вам не разрешено изменять ostream
, вы не можете этого сделать. Это оставляет только глобальную перегрузку в качестве альтернативы.
Тем не менее, существует довольно широко используемый шаблон, в котором вы перегружаете оператор глобально, но при этом вызываете функцию-член:
class whatever {
// make this public, or the global overload a friend.
std::ostream &write(std::ostream &dest) const {
// write self to dest
}
};
std::ostream &operator<<(std::ostream &os, whatever const &w) {
return w.write(os);
}
Это особенно полезно, когда / если вы хотите полиморфное поведение. Вы не можете сделать перегруженный оператор самим полиморфным, но вы делаете функцию-член, которую он вызывает virtual
, таким образом, она все равно действует полиморфно.
Редактировать: чтобы (надеюсь) прояснить ситуацию, вы можете сделать это несколькими различными способами. Первое и, вероятно, наиболее очевидное - это сделать общедоступным наш элемент write
и сделать так, чтобы глобальный оператор вызвал его. Поскольку это общедоступно, нам не нужно делать ничего особенного, чтобы позволить оператору использовать его:
class myClass {
public:
std::ostream &write(std::ostream &os) const {
// write stuff to stream
return os;
}
};
std::ostream &operator<<(std::ostream &os, myClas const &m) {
// since `write` is public, we can call it without any problem.
return m.write(os);
}
Вторая альтернатива - сделать write
приватным и объявить operator<<
другом, чтобы предоставить ему доступ:
class myClass {
// Note this is private:
std::ostream &write(std::ostream &os) const {
// write stuff to stream
return os;
}
// since `write` is private, we declare `operator<<` a friend to give it access:
friend std::ostream &operator<<(std::ostream &, myClass const &);
};
std::ostream &operator<<(std::ostream &os, myClas const &m) {
return m.write(os);
}
Третья возможность почти такая же, как и вторая:
class myClass {
// Note this is private:
std::ostream &write(std::ostream &os) const {
// write stuff to stream
return os;
}
// since `write` is private, we declare `operator<<` a friend to give it access.
// We also implement it right here inside the class definition though:
friend std::ostream &operator<<(std::ostream &os, myClas const &m) {
return m.write(os);
}
};
В этом третьем случае в C ++ используется довольно странное (и малоизвестное) правило, называемое «внедрение имени». Компилятор знает, что функция friend
не может быть частью класса, поэтому вместо определения функции-члена это «внедряет» имя этой функции в окружающую область (в данном случае, глобальную область). Даже если operator<<
определено внутри определения класса, это не функция-член вообще - это глобальная функция.