Cuando se sobrecarga como una función miembro, a << b
se interpreta como a.operator<<(b)
, por lo que solo toma un parámetro explícito (con this
como parámetro oculto).
Dado que esto requiere que la sobrecarga sea parte de la clase utilizada como el operando de la izquierda, no es útil con ostream
s normales y tal. Requeriría que su sobrecarga sea parte de la clase ostream
, no es parte de su clase. Como no puede modificar ostream
, no puede hacer eso. Eso deja solo la sobrecarga global como alternativa.
Hay, sin embargo, un patrón bastante ampliamente utilizada en el que sobrecargar el operador a nivel mundial, pero tienen que llamar a una función miembro:
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);
}
Esto es particularmente útil cuando/si quieres comportamiento polimórfico. No puedes hacer que el operador sobrecargado sea polimórfico, pero haces que la función miembro llame al virtual
, por lo que actúa polimórficamente de todos modos.
Editar: a (espero) aclarar la situación, puede hacerlo de diferentes maneras.El primero y probablemente el más obvio es hacer público nuestro miembro write
y hacer que el operador global lo llame. Dado que es pública, no tenemos que hacer nada especial para que el operador lo utilizan:
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);
}
Una segunda alternativa es hacer write
privada, y declarar operator<<
un amigo para darle acceso:
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);
}
Hay una tercera posibilidad que es casi igual que el segundo:
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);
}
};
Este tercer caso utiliza una regla bastante extraño (y poco conocido) en C + + llamado "inyección de nombre". El compilador sabe que una función friend
no puede ser parte de la clase, por lo que en lugar de definir una función miembro, esto "inyecta" el nombre de esa función en el ámbito circundante (el alcance global, en este caso). Aunque operator<<
se define dentro de la definición de clase, es no una función miembro en absoluto: es una función global.
¿qué ocurre si el 'operator <<' se declara como público en la clase pero no se implementa en la clase como 'MyClass :: operator <<'? ¿Se requiere 'amigo'? –
Interesante. Por lo tanto, puede declararse en la clase, implementarse como una función no miembro y no ser un amigo de la clase para seguir llamando a las funciones miembro públicas de esa clase. –
@ 0A0D: No estoy seguro de seguir. En mi comentario anterior, "eso" se refería a la función miembro. ¿Estás hablando de otra cosa? –