Ответ 1
При перегрузке как функции-члена 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
private и объявить 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);
}
};
Этот третий случай использует довольно странное (и малоизвестное) правило в С++ под названием "инъекция имени". Компилятор знает, что функция friend
не может быть частью класса, поэтому вместо определения функции-члена это "вводит" имя этой функции в окружающую область (глобальная область, в данном случае). Хотя operator<<
определяется внутри определения класса, он не является функцией-членом вообще - это глобальная функция.