c++ - valores - punteros como parametros de funciones en c




¿Por qué C++ 11 tiene movimientos implícitos para los parámetros de valor, pero no para los parámetros de valor? (2)

En C ++ 11, los parámetros de valor (y otros valores) disfrutan de un movimiento implícito cuando se devuelven:

A func(A a) {
    return a; // uses A::A(A&&) if it exists
}

Al menos en MSVC 2010, los parámetros de referencia de rvalue necesitan std::move :

A func(A && a) {
    return a; // uses A::A(A const&) even if A::A(A&&) exists
}

Me imagino que dentro de las funciones, una referencia rvalue y un valor se comportan de manera similar, con la única diferencia de que, en el caso de los valores, la función en sí misma es responsable de la destrucción, mientras que para las referencias rvalue, la responsabilidad está fuera.

¿Cuál es la motivación para tratarlos de manera diferente en la norma?


El comité de estandarización realizó un gran esfuerzo en la creación de una redacción para que los movimientos solo sucedieran exactamente en dos circunstancias:

  1. Cuando es claramente seguro hacerlo.
  2. Cuando el usuario pregunta explícitamente (a través de std::move o un reparto similar).

Un parámetro de valor será sin duda destruido 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 romper cosas deliberadamente, en cuyo caso probablemente haya desencadenado un comportamiento indefinido). Por lo tanto, se puede mover desde la devolución.

Una variable && podría referirse a una temporal. Pero podría estar refiriéndose a un lvalue (una variable con nombre). Por lo tanto, no es claramente seguro moverse de él; La variable original podría estar al acecho. Y como no solicitó explícitamente que se mueva (es decir, no llamó std::move en esta función), no se puede realizar ningún movimiento.

La única vez que una variable && moverá implícitamente (es decir, sin std::move ) es cuando la devuelve . std::move<T> devuelve un T&& . Es legal que el valor de retorno invoque al constructor de movimiento, porque es un valor de retorno.

Ahora es muy difícil llamar a A func(A &&a) con un lvalue sin llamar a std::move (o una conversión equivalente). Así que técnicamente, debería estar bien que los parámetros de tipo && se muevan 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 usar el conocimiento fuera de la función sobre de dónde viene el && .

En general, solo debe tomar los parámetros de && en dos casos: o está escribiendo un constructor de movimientos (o un operador de asignación de movimientos, pero incluso eso puede hacerse por valor), o está escribiendo una función de reenvío. Puede haber algunos otros casos, pero no debe llevar && a un tipo a menos que tenga algo especial en mente. Si A es un tipo móvil, simplemente tómelo por valor.


En su primer caso, el compilador sabe que a está yendo y nada podrá aferrarse a él: claramente, este objeto se puede mover y, de lo contrario, se destruirá. En el segundo caso, la referencia rvalue indica que está permitido moverse desde el objeto y la persona que llama no espera que el objeto se quede alrededor. Sin embargo, es la elección de la función si se aprovecha de este permiso o no y puede haber razones por las que la función a veces quiere moverse del argumento y otras no. Si al compilador se le diera la libertad de salir de este objeto, no habría manera de evitar que el compilador lo haga. Sin embargo, utilizando std::move(a) ya hay una manera de indicar que se desea mover desde el objeto.

La regla general en la norma es que el compilador solo mueve objetos de forma implícita que se sabe que desaparecen. Cuando entra una referencia rvalue, el compilador no sabe realmente que el objeto está a punto de desaparecer: si fue explícitamente std::move() ed, en realidad permanece alrededor.





rvalue-reference