2012-06-18 17 views
7

Sé que al pasar un objeto por valor a una función, el constructor de movimiento siempre se llama si hay uno, suponiendo que no se copia elisión. ¿Qué hay de devolver un objeto por valor?¿Se garantiza que un objeto se moverá cuando se devuelva?

Por ejemplo, supongamos que tenemos una clase Foo que tiene un constructor de movimiento, y tenemos una función que devuelve un objeto Foo.

Foo g() { 
    Foo f; 

    // do something with f 

    return f; 
} 

Si suponemos que no hay ningún RVO, ¿se garantiza que se llamará al constructor de movimiento?

Actualización: Creo que no mostré mi intención claramente. Solo quiero saber que puedo, en el peor de los casos, mover el objeto no copiado. O bien ocurre RVO o NRVO, estoy contento. Y también debería decir que el constructor de movimientos y la asignación de movimiento no se eliminan y se implementan correctamente.

+0

Sí, los objetos locales en el almacenamiento automático se tratan implícitamente como xvalues ​​en declaraciones de devolución. – ildjarn

+0

@ildjarn: Creo que es solo si lo devuelve directamente. En algún lugar en SO me corrigieron diciendo que esto movía 'f' a la función:' return do_something (f); '. – GManNickG

+0

@GManNickG: No tuve tiempo para el estándar o las advertencias, de ahí un comentario en lugar de una respuesta. Creo que tienes razón. : -] – ildjarn

Respuesta

4

Sí. Ver [clase.copia] p32

Cuando se cumplen los criterios para la elisión de una operación de copia o se cumplirían, salvo por el hecho de que el objeto de origen es un parámetro de función, y el objeto que se va a copiar se designa por un lvalue, sobrecarga la resolución para seleccionar el constructor para la copia se realiza primero como si el objeto fuera designado por un valor r. Si la resolución de sobrecarga falla, o si el tipo del primer parámetro del constructor seleccionado no es una referencia rvalue a el 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. - nota final]

+0

+1 para la cita estándar necesaria. – ildjarn

2

En este caso, dado que el valor de retorno tiene un nombre (f), sería NRVO (denominado optimización del valor de retorno) que se aplicaría.

Por lo tanto, la respuesta técnica basada únicamente en la redacción, es que la ausencia de RVO no impedirá la elisión de copia, ya que NRVO todavía podría permitirlo.

Pasado eso, creo que la selección entre mover/copiar del valor de retorno puede/dependerá de la definición de Foo - definitivamente hay momentos en que se copiará en lugar de moverse, como si hubiera explícitamente eliminó el constructor de movimiento y los operadores de asignación de movimiento, o no ha definido la construcción/asignación de movimiento, y no es elegible para que se sinteticen implícitamente.

Editar: [responder a la pregunta editada]: Tener un constructor de movimientos todavía no garantiza que el resultado se moverá. Un ejemplo obvio sería si hubiera eliminado el operador de asignación de movimiento y estuviera asignando el resultado (en lugar de usarlo para inicializar). En este caso, el operador de asignación de movimiento eliminado evitaría mover el valor de retorno.

Sin embargo, para responder a lo que pudo haber estado haciendo, la regla general es que el traslado se realizará si es posible, y se recurrirá a la copia solo si algo impide que se mueva el resultado.

+0

Supongo que no mostré mi intención claramente en la pregunta. Solo quiero saber que puedo, en el peor de los casos, mover el objeto no copiado. O bien ocurre RVO o NRVO, estoy contento. Y también debería decir que el constructor de movimiento y la asignación de movimiento no se eliminan. Gracias. – haotang

+3

@Jerry: hay dos copias potenciales en ese código. El primero es de la variable local a la declaración de devolución, que comúnmente se conoce como (N) RVO. Eso está garantizado para no copiar si hay un constructor de movimiento, el compilador puede aplicar (N) RVO o no, pero si no lo hace * debe * mover constructo. La segunda copia potencial está en el lado del llamante, desde el objeto devuelto, y eso queda fuera del alcance de la pregunta, ya que depende del código de la persona que llama, no de la función. –

1

La regla es que siempre que se permite la elisión de copia pero no ocurre, el constructor de movimientos se usará si está disponible, y de lo contrario se usará el constructor de copias.

El comportamiento exacto está definido por [class.copy]/32:

Cuando se cumplen los criterios para la elisión de una operación de copia o se cumplirían, salvo por el hecho de que el objeto de origen es un parámetro de función, y el objeto a ser copiado es designado por un lvalue, la resolución de sobrecarga para seleccionar el constructor para la copia se realiza primero como si el objeto fuera designado por un valor r. 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.

Cuestiones relacionadas