Nota: esta respuesta solo se aplica a c ++ 11 en adelante. No existe "C / C ++", son lenguajes diferentes.
No, no existe ningún peligro en devolver un objeto local por valor, y se recomienda hacerlo. Sin embargo, creo que hay un punto importante que falta en todas las respuestas aquí. Muchos otros han dicho que la estructura se copia o se coloca directamente mediante RVO. Sin embargo, esto no es del todo correcto. Intentaré explicar exactamente qué cosas pueden suceder al devolver un objeto local.
Mover semántica
Desde c ++ 11, hemos tenido referencias rvalue que son referencias a objetos temporales que pueden ser robados de forma segura. Como ejemplo, std :: vector tiene un constructor de movimiento así como un operador de asignación de movimiento. Ambos tienen una complejidad constante y simplemente copian el puntero a los datos del vector que se está moviendo. No entraré en más detalles sobre la semántica de movimientos aquí.
Debido a que un objeto creado localmente dentro de una función es temporal y sale del alcance cuando la función regresa, un objeto devuelto nunca se copia con c ++ 11 en adelante. Se llama al constructor de movimiento en el objeto que se devuelve (o no, se explica más adelante). Esto significa que si devolviera un objeto con un constructor de copia costoso pero un constructor de movimiento económico, como un vector grande, solo la propiedad de los datos se transfiere del objeto local al objeto devuelto, lo cual es barato.
Tenga en cuenta que en su ejemplo específico, no hay diferencia entre copiar y mover el objeto. Los constructores de movimiento y copia predeterminados de su estructura dan como resultado las mismas operaciones; copiando dos enteros. Sin embargo, esto es al menos tan rápido que cualquier otra solución porque toda la estructura cabe en un registro de CPU de 64 bits (corríjame si me equivoco, no conozco muchos registros de CPU).
RVO y NRVO
RVO significa optimización del valor de retorno y es una de las pocas optimizaciones que hacen los compiladores y que puede tener efectos secundarios. Desde c ++ 17, se requiere RVO. Cuando se devuelve un objeto sin nombre, se construye directamente en el lugar donde la persona que llama asigna el valor devuelto. No se llama ni al constructor de copia ni al constructor de movimiento. Sin RVO, el objeto sin nombre se construiría primero localmente, luego se movería construido en la dirección devuelta y luego se destruiría el objeto sin nombre local.
Ejemplo donde se requiere RVO (c ++ 17) o probable (antes de c ++ 17):
auto function(int a, int b) -> MyStruct {
return MyStruct{a, b};
}
NRVO significa Optimización del valor de retorno con nombre y es lo mismo que RVO, excepto que se realiza para un objeto con nombre local a la función llamada. Esto todavía no está garantizado por el estándar (c ++ 20) pero muchos compiladores aún lo hacen. Tenga en cuenta que incluso con objetos locales con nombre, en el peor de los casos se mueven cuando se devuelven.
Conclusión
El único caso en el que debería considerar no devolver por valor es cuando tiene un objeto con nombre, muy grande (como en su tamaño de pila). Esto se debe a que NRVO aún no está garantizado (a partir de c ++ 20) e incluso mover el objeto sería lento. Mi recomendación, y la recomendación en las Pautas básicas de Cpp es siempre preferir devolver objetos por valor (si hay múltiples valores de retorno, use struct (o tupla)), donde la única excepción es cuando el objeto es costoso de mover. En ese caso, use un parámetro de referencia que no sea constante.
NUNCA es una buena idea devolver un recurso que tiene que ser liberado manualmente desde una función en c ++. Nunca hagas eso. Al menos use un std :: unique_ptr, o cree su propia estructura local o no local con un destructor que libere su recurso ( RAII ) y devuelva una instancia de eso. Entonces también sería una buena idea definir el constructor de movimiento y el operador de asignación de movimiento si el recurso no tiene su propia semántica de movimiento (y eliminar el constructor de copia / asignación).