2012-03-02 14 views
21

Soy consciente de que no es normalmente una buena idea volver con std::move, es decir¿Está regresando con `std :: move` sensible en el caso de declaraciones de devolución múltiples?

bigObject foo() { bigObject result; /*...*/ return std::move(result); } 

en lugar de simplemente

bigObject foo() { bigObject result; /*...*/ return result; } 

porque se pone en el camino de la optimización del valor de retorno. Pero lo que en el caso de una función con múltiples retornos diferentes, sobre todo algo así como

class bar { 
    bigObject fixed_ret; 
    bool use_fixed_ret; 
    void prepare_object(bigObject&); 
public: 
    bigObject foo() { 
    if(use_fixed_ret) 
     return fixed_ret; 
    else{ 
     bigObject result; 
     prepare_object(result); 
     return result; 
    } 
    } 
}; 

creo optimización normales valor de retorno es imposible en tal función, por lo que sería una buena idea poner en

 return std::move(result); 

aquí, o más bien debería hacer (OMI más feo, pero eso es debatible)

bigObject foo() { 
    bigObject result; 
    if(use_fixed_ret) 
     result = fixed_ret; 
    else{ 
     prepare_object(result); 
    } 
    return result; 
    } 
+0

No puedo hacer referencia al estándar, así que no responderé, pero estoy seguro de que no necesitas std :: move, creo que estás confundiendo RVO y copia elisión, copia elisión es la optimización que beneficia a algunos compiladores que tienen una única ruta de retorno. BigObject se convertirá en un valor R respecto a dónde regresó. – 111111

+0

Sí, creo que copiar elisión es lo que quiero decir al principio. – leftaroundabout

+1

Además, si le gusta el código limpio y las optimizaciones del nivel de código parecen, :), puede deshacerse de la rama 'else {...' en su función 'foo()'. Como si la primera afirmación fuera cierta, la segunda no será evaluada. – 111111

Respuesta

33

para las variables locales, no hay necesidad de std::move ellos en el return comunicado mayoría de las veces , ya que el lenguaje realidad exige que esto sucede de forma automática:

§12.8 [class.copy] p32

cuando se cumplen los criterios para la elisión de una operación de copia o se cumpliría, salvo por el hecho de que el objeto fuente es un parámetro de función, y el objeto que se va a copiar se designa con un lvalue, la resolución de sobrecarga para seleccionar el constructor para la copia se realiza primero como si el objeto se designara con un valor. Si la resolución de sobrecarga falla, o si el tipo del primer parámetro del constructor seleccionado no es una referencia rvalue al tipo del objeto (posiblemente cv-qualified), la resolución de sobrecarga se realiza nuevamente, considerando el objeto como un valor l. [Nota: Esta resolución de sobrecarga de dos etapas debe realizarse independientemente de si se producirá elisión de copia. Determina el nombre del constructor que se invocará si no se realiza una elisión, y el constructor seleccionado debe estar accesible incluso si se elimina la llamada. -fin nota]


† Copia elisión es muy restringido en donde se puede aplicar (§12.8/31). Una de estas restricciones es que el tipo de objeto fuente debe ser el mismo que el tipo de retorno cv no calificado de la función cuando se trata de una declaración de retorno. Tampoco es aplicable para subobjetos de variables locales que están a punto de quedar fuera del alcance.

+2

Y aquí está su referencia de estándares. – 111111

+1

Muy bien ... Siempre tengo mis dificultades con las formulaciones del estándar, lo leo cinco veces y todavía no entiendo muy bien lo que se supone que significa. ¿Pero dices, en mi ejemplo, simplemente significa "la primera versión es tan buena como es posible"? Entonces confiaré en ti allí. – leftaroundabout

+2

@leftaroundabout: Básicamente dice "Si el objeto es local y se devuelve por valor, primero intente devolverlo como un valor r (como lo haría' std :: mover'). Si eso no encuentra un movimiento, escriba intente lo mismo, pero esta vez como un lvalue. – Xeo

Cuestiones relacionadas