¿Es más eficiente la devolución por referencia de valor?

Respuestas:

246
Beta_ab&&
Beta::toAB() const {
    return move(Beta_ab(1, 1));
}

Esto devuelve una referencia colgante, al igual que con el caso de referencia lvalue. Después de que la función regrese, el objeto temporal se destruirá. Debe devolver Beta_abpor valor, como el siguiente

Beta_ab
Beta::toAB() const {
    return Beta_ab(1, 1);
}

Ahora, está moviendo correctamente un Beta_abobjeto temporal al valor de retorno de la función. Si el compilador puede, evitará el movimiento por completo, utilizando RVO (optimización del valor de retorno). Ahora puedes hacer lo siguiente

Beta_ab ab = others.toAB();

Y se moverá a construir lo temporal ab, o hará RVO para omitir hacer un movimiento o copiar por completo. Le recomiendo leer BoostCon09 Rvalue References 101 que explica el asunto y cómo (N) RVO interactúa con esto.


Su caso de devolver una referencia de valor sería una buena idea en otras ocasiones. Imagine que tiene una getAB()función que a menudo invoca de forma temporal. No es óptimo hacer que devuelva una referencia constante de valor para los valores temporales de valor. Puedes implementarlo así

struct Beta {
  Beta_ab ab;
  Beta_ab const& getAB() const& { return ab; }
  Beta_ab && getAB() && { return move(ab); }
};

Tenga moveen cuenta que en este caso no es opcional, ya abque no es un valor local automático ni temporal. Ahora, el calificador de referencia && dice que la segunda función se invoca en los valores temporales de valor, haciendo el siguiente movimiento, en lugar de copiar

Beta_ab ab = Beta().getAB();
Johannes Schaub - litb
fuente
51
Siempre supuse que el problema de referencia pendiente desaparecía automáticamente cuando el tipo de retorno era una referencia de valor r. Me alegro de haberlo aclarado antes de que me mordiera. La pila de bichos aplastantes apesta.
deft_code el
31
:) Realmente, las referencias de valor son "solo referencias" como lo son las referencias de valor. No copian ni almacenan nada.
Johannes Schaub - litb
9
¿Qué funciona más const y calificador en un miembro que un simple const?
galinette
44
+ 1-ed, pero enlace roto: BoostCon09 Rvalue References 101
Siu Ching Pong -Asuka Kenji-
3
@galinette Estos son calificadores de referencia .
Malcolm
2

Se puede ser más eficiente, por ejemplo, en un contexto diferente bits:

template <typename T>
T&& min_(T&& a, T &&b) {
    return std::move(a < b? a: b);
}

int main() {
   const std::string s = min_(std::string("A"), std::string("B"));
   fprintf(stderr, "min: %s\n", s.c_str());
   return 0;
}

Como observación interesante, en mi máquina clang++ -O3genera 54 instrucciones para el código anterior versus 62 instrucciones para el código regular std::min. Sin embargo, con -O0esto genera 518 instrucciones para el código anterior versus 481 para el código regular std::min.

wonder.mice
fuente
Estoy confundido por tu respuesta. Había intentado una versión similar (quizás) pero falló: ideone.com/4GyUbZ ¿Podría explicar por qué?
Deqing
Usó referencia en objeto temporal for(:)e integración sobre objeto desasignado. Solución: ideone.com/tQVOal
wonder.mice
3
¿No es esta respuesta realmente incorrecta? para el parámetro de plantilla T, T&& no es una referencia de valor r, sino más bien una referencia universal, y para ese caso, siempre deberíamos llamar a std :: forward <T>, no std :: move! Sin mencionar que esta respuesta contradice directamente la más votada arriba.
xdavidliu
@xdavidliu esta respuesta es un ejemplo artificial de cómo regresar por rvalue puede ser más eficiente. std::move()solo se usa como un reparto explícito para ilustrar el punto más claramente. No es un código que copiarías y pegarías en tu proyecto. No contradice la respuesta más votada, porque se crea un objeto temporal dentro de la función. Aquí el objeto devuelto es uno de los argumentos (los objetos temporales se destruyen como el último paso para evaluar la expresión completa que (léxicamente) contiene el punto donde fueron creados).
wonder.mice
2
@ wonder.mice, reemplace T con std :: string; no hay necesidad de usar plantillas aquí, y usar T&& como referencia de valor r es simplemente un estilo horrible que confunde innecesariamente a las personas nuevas en plantillas y valores r.
xdavidliu