Devuelve 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. Eso por sí solo debería ser razón suficiente para no regresar por referencia. Las interfaces deben ser sólidas.
La preocupación por el costo es hoy en día discutible gracias a la optimización del valor de retorno (RVO), por lo que no incurrirá en una secuencia de incremento-incremento-decremento o algo así en los compiladores modernos. Entonces, la mejor manera de devolver un shared_ptr
es simplemente regresar por valor:
shared_ptr<T> Foo()
{
return shared_ptr<T>(/* acquire something */);
};
Esta es una oportunidad RVO obvia para los compiladores modernos de C ++. Sé a ciencia cierta que los compiladores de Visual C ++ implementan RVO incluso cuando todas las optimizaciones están desactivadas. Y con la semántica de movimientos 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 un artículo que argumenta a favor de la devolución por valor. Reproduzco un fragmento aquí; Le recomiendo que lea el artículo completo:
Sea honesto: ¿cómo le hace sentir el siguiente código?
std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();
Francamente, aunque debería saberlo mejor, me pone nervioso. En principio, cuando get_names()
regrese, tenemos que copiar una vector
de string
s. Luego, necesitamos copiarlo nuevamente cuando inicializamos
names
, y necesitamos destruir la primera copia. Si hay N string
s en el vector, cada copia podría requerir hasta N + 1 asignaciones de memoria y una gran cantidad de accesos de datos no amigables con la caché> a medida que se copia el contenido de la cadena.
En lugar de enfrentarme a ese tipo de ansiedad, a menudo recurro al paso por referencia para evitar copias innecesarias:
get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );
Desafortunadamente, este enfoque está lejos de ser ideal.
- El código creció un 150%
- Hemos tenido que abandonar
const
porque estamos mutando nombres.
- Como a los programadores funcionales les gusta recordarnos, 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 una semántica de valores estricta para los nombres.
Pero, ¿es realmente necesario estropear nuestro código de esta manera para ganar eficiencia? Afortunadamente, la respuesta resulta ser no (y especialmente no si está usando C ++ 0x).