Nel caso specifico di un ostream come cout, nella catena di derivazione, è disabilitato il passaggio per valore. (non ricordo in che punto, forse ios)
http://www.cplusplus.com/reference/iostream/

Nel caso l'operatore fosse definito per due classi differenti, si ricade sempre nel doppio passaggio per valore con annessi e connessi.
Costruttore di copia all'ingresso e all'uscita della funzione e se la classe contiene raw pointer, occorre fare molta attenzione.

C'è poi un altro caso.
codice:
class A {
   public:
       virtual void p() { cout << "A" << endl; } 
};

class B : public A {
   public:
       void p() { cout << "B" << endl; } 
};
void test(A a) { a.p(); }

main() {
   B b;
   test(b);
}
test stamperà "A" , non B in quanto si verifica lo slicing della classe.