Puoi anche non dichiararla friend, può anche essere una semplice funzione esterna che riesca ad accedere ai campi dell'oggetto messo come secondo argomento (attraverso per esempio un getter) per poterli stampare.
#include <iostream>
using namespace std;
class Complex {
private:
int real;
int img;
public:
Complex(int r=0,int i=0) {
real=r;
img=i;
}
int getReal() {
return real;
}
int getImg() {
return img;
}
};
ostream & operator<<(ostream &out,Complex &c) {
out<<c.getReal()<<"+i"<<c.getImg()<<endl;
return out;
}
int main() {
Complex c(10,5);
cout<<c<<endl;
}
Non puoi invece metterla come funzione membro di una classe, perché la funzione deve essere chiamata su un oggetto di tipo ostream, mentre invece se la metti dentro dovresti chiamarla su un oggetto di quella classe che non avrebbe senso.