2010-11-03 19 views
29

Cuando tiene un objeto derivado con un constructor de movimiento, y el objeto base también tiene semántica de movimiento, ¿cuál es la forma correcta de llamar al constructor de movimiento de objeto base desde el constructor de movimiento de objeto derivado?Mover constructor en objeto derivado

probé la primera cosa más obvia:

Derived(Derived&& rval) : Base(rval) 
{ } 

Sin embargo, esto parece acabar llamando a copia constructor del objeto Base. Luego probé explícitamente mediante std::move aquí, así:

Derived(Derived&& rval) : Base(std::move(rval)) 
{ } 

Esto funcionó, pero estoy confundido por qué es necesario. Pensé que std::move simplemente devuelve una referencia rvalue. Pero dado que en este ejemplo rval ya es una referencia rvalue, la llamada a std::move debería ser superflua. Pero si no uso std::move aquí, simplemente llama al constructor de copia. Entonces, ¿por qué es necesaria la llamada al std::move?

Respuesta

29

rval no es un Rvalue. Es un Lvalue dentro del cuerpo del constructor de movimientos. Es por eso que debemos invocar explícitamente a std :: move.

Consulte this. La nota importante es

nota anterior que el argumento x es tratado como un valor-I interna a las funciones movimiento, a pesar de que es declarado como un parámetro de referencia rvalue. Es por eso que es necesario decir move (x) en lugar de solo x cuando pasa a la clase base. Esta es una característica clave de seguridad de la semántica del movimiento diseñada para evitar que se mueva accidentalmente dos veces desde alguna variable con nombre . Todos los movimientos ocurren solo desde valores r, o con un molde explícito para valorizar como usar std :: mover. Si tiene un nombre para la variable, es un valor l.

+0

Solo para agregar una nota independientemente del constructor de movimiento si proporciona una entrada de argumento como lvalue regular (no utiliza std :: move (xx)) obtendrá "no se puede unir" Xyy 'lvalue a' Xyy && 'para una función definido como foo (Xyy && xyy). –

0

Realmente debería usar std :: forward (obj) en lugar de std :: move (obj). Forward devolverá el rvalue o lvalue adecuado en función del obj que es mientras que move cambiará un lvalue a un rvalue.

+10

En este contexto, siempre desea convertir a un valor r. –

+2

Por lo tanto, la intención real es enviar exactamente lo que se pasó para no cambiar lo que se pasó. Reenviar () hace exactamente eso. también lo pondría en la práctica de usarlo para llamar a todas las funciones básicas sin importar lo que se transmita. Tener el hábito de llamar a move() podría generar errores que podrían ser difíciles de rastrear. Esto también es útil si quisiera utilice plantillas en una función que pueda actuar tanto en un valor de referencia como en un valor de l. Si esta opción se pasó por error en un objeto que es un valor l, querría que ¿ejecución continua? Simplemente jugando a los diablos abogar un poco – jbreiding

+1

@jbreiding No entiendo nada de esto ... mente elaborando en su explicación con algunos ejemplos de código en cuando std :: move fallará, por qué se prefiere std :: forward y cuándo usar qué? – aCuria

Cuestiones relacionadas