Restituisce puntatori intelligenti per valore.
Come hai detto, se lo restituisci per riferimento, non aumenterai correttamente il conteggio dei riferimenti, il che espone al rischio di eliminare qualcosa al momento sbagliato. Questo da solo dovrebbe essere un motivo sufficiente per non tornare per riferimento. Le interfacce dovrebbero essere robuste.
La preoccupazione dei costi è oggigiorno discutibile grazie all'ottimizzazione del valore di ritorno (RVO), quindi non incorrerai in una sequenza di incremento-incremento-decremento o qualcosa del genere nei compilatori moderni. Quindi il modo migliore per restituire a shared_ptr
è semplicemente restituire per valore:
shared_ptr<T> Foo()
{
return shared_ptr<T>(/* acquire something */);
};
Questa è un'ovvia opportunità RVO per i moderni compilatori C ++. So per certo che i compilatori Visual C ++ implementano RVO anche quando tutte le ottimizzazioni sono disattivate. E con la semantica di spostamento di C ++ 11, questa preoccupazione è ancora meno rilevante. (Ma l'unico modo per essere sicuri è profilare e sperimentare.)
Se non sei ancora convinto, Dave Abrahams ha un articolo che sostiene il ritorno in base al valore. Riporto qui uno snippet; Consiglio vivamente di leggere l'intero articolo:
Sii onesto: come ti fa sentire il codice seguente?
std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();
Francamente, anche se dovrei saperlo meglio, mi rende nervoso. In linea di principio, quando get_names()
ritorna, dobbiamo copiare a vector
di string
s. Quindi, dobbiamo copiarlo di nuovo quando inizializziamo
names
e dobbiamo distruggere la prima copia. Se ci sono N string
nel vettore, ogni copia potrebbe richiedere fino a N + 1 allocazioni di memoria e tutta una serie di accessi ai dati non compatibili con la cache> man mano che il contenuto della stringa viene copiato.
Piuttosto che affrontare quel tipo di ansia, sono spesso tornato sul pass-by-reference per evitare copie inutili:
get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );
Sfortunatamente, questo approccio è tutt'altro che ideale.
- Il codice è cresciuto del 150%
- Abbiamo dovuto abbandonare la
const
questione perché stiamo mutando i nomi.
- Come piace ricordarci ai programmatori funzionali, la mutazione rende il codice più complesso da ragionare minando la trasparenza referenziale e il ragionamento equazionale.
- Non abbiamo più una semantica di valori rigidi per i nomi.
Ma è davvero necessario rovinare il nostro codice in questo modo per guadagnare efficienza? Fortunatamente, la risposta risulta essere no (e soprattutto non se stai usando C ++ 0x).