Devuelva los punteros inteligentes por valor.
Como ha dicho, si lo devuelve por referencia, no aumentará correctamente el recuento de referencias, lo que abre el riesgo de eliminar algo en el momento inadecuado. Solo eso debería ser motivo suficiente para no regresar por referencia. Las interfaces deben ser robustas.
La preocupación por el costo es actualmente discutible gracias a return value optimization (RVO), por lo que no incurrirá en una secuencia de incremento-incremento-decremento o algo así en los compiladores modernos. Así que la mejor manera de devolver un shared_ptr
es simplemente volver por valor:
shared_ptr<T> Foo()
{
return shared_ptr<T>(/* acquire something */);
};
Esta es una oportunidad RVO-muertos obvio para los modernos compiladores de C++. Sé con certeza que los compiladores de Visual C++ implementan RVO incluso cuando todas las optimizaciones están desactivadas. Y con la semántica del movimiento de C++ 11, esta preocupación es aún menos relevante. (Pero la única forma de estar seguro es perfilar y experimentar)
Si aún no está convencido, Dave Abrahams tiene an article que hace un argumento para devolver por valor. Reproduzco un fragmento aquí; Le recomiendo que lea el artículo completo:
Sea sincero: ¿cómo se siente el siguiente código?
std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();
Francamente, aunque debería saber mejor, me pone nervioso. En principio, cuando devuelve get_names()
, tenemos que copiar un vector
de string
s. Entonces, tenemos que copiarlo nuevamente cuando inicialicemos names
, y tenemos que destruir la primera copia. Si hay N string
s en el vector, cada copia podría requerir tantas como N + 1 asignaciones de memoria y una gran cantidad de accesos a datos no compatibles con la memoria caché> a medida que se copian los contenidos de la cadena.
En lugar de enfrentarse a ese tipo de ansiedad, a menudo he vuelto a caer en el paso por referencia para evitar copias innecesarias :
get_names(std::vector<std::string>& out_param);
...
std::vector<std::string> names;
get_names(names);
Por desgracia, este enfoque está lejos de ser ideal.
- El código creció en un 150%
- Hemos tenido que dejar
const
-ness porque estamos mutando nombres.
- Como los programadores funcionales nos recuerdan, la mutación hace que el código sea más complejo para razonar al socavar la transparencia referencial y el razonamiento ecuacional.
- Ya no tenemos semántica de valor estricto para los nombres.
¿Pero es realmente necesario ensuciar nuestro código de esta manera para ganar eficiencia? Afortunadamente, la respuesta resulta ser no (y especialmente no si está usando C++ 0x).
No sé si yo diría que RVO hace que la pregunta sea discutible ya que regresar por referencia hace que RVO sea imposible. –
@CrazyEddie: Es cierto que esa es una de las razones por las que recomiendo que el OP devuelva por valor. –
¿La regla de RVO, permitida por el estándar, prevalece sobre las reglas sobre sincronización/relaciones de pase previo garantizadas por el estándar? –