2012-08-14 12 views
6

uso el código de abajo a prueba de copia elisión:¿Por qué copiar elision no funciona con std :: move?

class foo 
{ 
public: 
    foo() {cout<<"ctor"<<endl;}; 
    foo(const foo &rhs) {cout<<"copy ctor"<<endl;} 
}; 

int g(foo a) 
{ 
    return 0; 
} 

int main() 
{ 
    foo a; 
    g(std::move(a)); 
    return 0; 
} 

que esperaba sólo el constructor por defecto sería llamado porque el argumento de g() es un valor de lado derecho y de la copia se elide. Pero el resultado muestra que se llama tanto al constructor predeterminado como al constructor de copia. ¿Por qué?

Y si cambio la función llame al g(foo()), la copia será eliminada. ¿Cuál es la diferencia entre los tipos de devolución de foo() y std::move(a)? ¿Cómo puedo hacer que el compilador elite copie en un lvalue?

+4

No puede.'g' toma su parámetro por valor para que el compilador tenga que asegurarse de que el objeto pasado sea distinto de cualquier objeto accesible desde el alcance de la llamada. Si el objeto que se pasa es un lvalue, no hay temporal que eliminar y no se puede eliminar una copia. –

+1

¿Cuántas llamadas a destructor esperaba? ;) – curiousguy

+0

Es posible que desee [leer] (http://stackoverflow.com/a/11540204/252000) en lo que 'std :: move' realmente hace. – fredoverflow

Respuesta

6

La elisión de copia para solo puede ocurrir en algunas situaciones específicas, la más común de las cuales es la copia de un temporal (los otros son locales que regresan y excepciones de lanzamiento/captura). Su código no produce ningún elemento temporal, por lo que no se elimina ninguna copia.

El constructor de copia está siendo llamado porque foo no tiene un constructor movimiento (constructores movimiento no se generan de forma implícita para las clases con constructores de copia explícitas), y así std::move(a) coincide con el foo(const foo &rhs) constructor (que se utiliza para construir el argumento de la función) .

Una copia de un valor-I se puede elided en las siguientes situaciones (aunque no hay manera de fuerza un compilador para realizar la elisión):

foo fn() { 
    foo localAutomaticVariable; 
    return localAutomaticVariable; //Copy to construct return value may be elided 
} 

int main() { 
    try { 
     foo localVariable; 
     throw localVariable; //The copy to construct the exception may be elided 
    } 
    catch(...) {} 
} 

Si se quiere evitar copias al pasar a la función argumentos, se puede utilizar un constructor de movimiento que hurta los recursos de los objetos dados a ella:

class bar { 
public: 
    bar() {cout<<"ctor"<<endl;}; 
    bar(const bar &rhs) {cout<<"copy ctor"<<endl;} 
    bar(bar &&rhs) {cout<<"move ctor"<<endl;} 
}; 

void fn(bar a) 
{ 
} 
//Prints: 
//"ctor" 
//"move ctor" 
int main() 
{ 
    bar b; 
    f(std::move(b)); 
} 

Además, cada vez que se permite la copia elisión, pero no se produce, se utiliza el movimiento del constructor si es disp le.

+0

+1, puede valer la pena señalar que, por defecto, no existe un constructor de movimiento, por lo que la construcción del argumento tuvo que recurrir al constructor de copia, incluso como valor r. – KillianDS

4

tiene que declarar como g:

int g(foo && a) //accept argument as rvalue reference 
{ 
    return 0; 
} 

Ahora se puede aceptar el argumento por valor p-referencia.

En su caso, aunque la expresión std::move(a) produce rvalue, no se enlaza a un parámetro que acepta el argumento por el valor. El extremo receptor debe ser rvalue-reference también.

En el caso de g(foo()), el compilador realiza la copia-elisión, que es una optimización. NO es un requisito por el idioma[until C++17]. Puede deshabilitar esta optimización si lo desea: entonces g(foo()) y g(std::move(a)) se comportarán exactamente igual, como se esperaba.

Pero si cambia g como he sugerido anteriormente, la llamada g(foo()) no hará una copia porque es un requisito de la lengua para no hacer copia con & &. Ya no es una optimización del compilador.

+5

Creo que te estás perdiendo el sentido de la pregunta. No habrá copia elisión con esa firma, simplemente porque no habrá copia. – hvd

+0

@hvd: No entendí tu comentario. ¿Cuál es la base de esto: * "No habrá copia elisión con esa firma, simplemente porque no habrá copia" *? – Nawaz

+2

Se podría argumentar que copy-ellision es, por definición, una optimización realizada por el compilador (aunque no estoy seguro si eso es correcto). En ese caso, su solución no es copia-ellision. –

Cuestiones relacionadas