El comité de normalización gastaron gran esfuerzo en la creación de redacción de manera que se mueve sólo ocurriría nunca en exactamente dos circunstancias:
- Cuando es claramente sea seguro hacerlo.
- Cuando el usuario explícitamente pregunta (a través de
std::move
o un modelo similar).
Un parámetro de valor se destruirá indudablemente al final de la función. Por lo tanto, devolverlo por movimiento es claramente seguro; no puede ser tocado por otro código después de la devolución (no a menos que intente deliberadamente romper las cosas, en cuyo caso probablemente desencadenó un comportamiento indefinido). Por lo tanto, se puede mover desde la devolución.
Una variable &&
podría estar refiriendo a un temporal. Pero podría estar refiriéndose a un lvalue (una variable nombrada). Por lo tanto, no es claramente seguro para moverse desde él; la variable original podría estar al acecho. Y como no hizo explícitamente solicite pasar de ella (es decir: no llamó al std::move
en esta función), no se puede realizar ningún movimiento.
La única vez que una variable &&
se moverá de forma implícita (es decir: sin std::move
) es cuando regrese ella. std::move<T>
devuelve un T&&
. Es legal que ese valor devuelto invoque el constructor de movimiento, porque es un valor de retorno.
Ahora es muy difícil llamar A func(A &&a)
con un valor- sin llamar std::move
(o un reparto equivalente). Por lo tanto, técnicamente, debería estar bien para los parámetros del tipo &&
del que se va a mover implícitamente. Pero el comité de estándares quería que los movimientos fueran explícitos para los tipos &&
, solo para asegurarse de que el movimiento no ocurriera implícitamente dentro del alcance de esta función. Es decir, no puede utilizar el conocimiento fuera de función sobre el origen del &&
.
En general, solo debe tomar los parámetros &&
en dos casos: o bien está escribiendo un constructor de movimiento (o un operador de asignación de movimiento, pero incluso eso puede hacerse por valor), o está escribiendo una función de reenvío . Puede haber algunos otros casos, pero no debe tomar &&
a menos que tenga algo especial en mente.Si A
es un tipo movible, simplemente tómalo por valor.
Si tiene una función 'A f();', si llama a 'a (f())' se llamaría 'a (A &&)' (ya que lo está llamando con un temporal)? Nunca dejé en claro las reglas sobre esto tampoco. –
@Seth, en resumen, sí. Considera 'A f()', que devuelve una instancia de 'A' por valor. Al hacerlo, no garantiza que ni las funciones a las que llama puedan contener una referencia a esa instancia. Ahora, 'a (f())' pasa inmediatamente esa instancia a 'a()', sin dar al que llama la posibilidad de mantener una referencia a él mismo (las cosas serían muy diferentes si emitiera 'A my_a; a (my_a = f()); 'por ejemplo). Pero en su situación, es perfectamente seguro que 'a (A &&)' sea llamado, y realmente tiene prioridad sobre 'a (const A &)' IIRC. –
Creo que tu respuesta es un poco ambigua. Esperaba una declaración clara de uno de los siguientes: (1) los parámetros '&&' * deben * moverse implícitamente, o (2) * no * se deben mover implícitamente, o bien (3) es una elección arbitraria para el compilador. Creo que, en este caso, el movimiento implícito es obligatorio y la versión de MSVC es incorrecta. 'return x;' debe ser reescrito implícitamente como 'return move (x);', donde 'x' es un parámetro, y' x' es 'A &&', (¡y 'A' no es un tipo de referencia en sí mismo!), y el tipo de devolución es 'A' (o incluso' A && '?). Creo que eso está garantizado. –