La función C ++ 11 std::move(x)
realmente no mueve nada en absoluto. Es solo un reparto al valor r. ¿Por qué se hizo esto? ¿No es esto engañoso?
c++
c++11
move-semantics
rvalue-reference
c++-faq
Howard Hinnant
fuente
fuente
std::move
realmente se mueve ...std::char_traits::move
std::remove()
que no elimina los elementos: aún debe llamarerase()
para eliminar realmente esos elementos del contenedor. Entoncesmove
no se mueve,remove
no se quita. Hubiera elegido el nombremark_movable()
paramove
.mark_movable()
confuso. Sugiere que hay un efecto secundario duradero donde de hecho no hay ninguno.Respuestas:
Es correcto que
std::move(x)
sea solo un yeso para rvalue, más específicamente para un xvalue , en lugar de un prvalue . Y también es cierto que tener un elenco llamado amove
veces confunde a las personas. Sin embargo, la intención de este nombre no es confundir, sino hacer que su código sea más legible.La historia
move
se remonta a la propuesta de mudanza original en 2002 . Este artículo presenta primero la referencia de valor, y luego muestra cómo escribir un método más eficientestd::swap
:Hay que recordar que en este punto de la historia, lo único que "
&&
" podría significar era lógico y . Nadie estaba familiarizado con las referencias de valor, ni con las implicaciones de transmitir un valor a un valor (sin hacer una copia comostatic_cast<T>(t)
lo haría). Entonces, los lectores de este código naturalmente pensarían:Tenga en cuenta también que en
swap
realidad es solo un sustituto para todo tipo de algoritmos de modificación de permutación. Esta discusión es mucho , mucho más grande queswap
.Luego, la propuesta introduce el azúcar de sintaxis que reemplaza el
static_cast<T&&>
con algo más legible que transmite no el qué exacto , sino el por qué :Es decir,
move
es sintaxis de azúcar parastatic_cast<T&&>
, y ahora el código es bastante sugerente de por qué esos lanzamientos están ahí: ¡para permitir la semántica de movimiento!Hay que entender que, en el contexto de la historia, pocas personas en este punto realmente entendieron la conexión íntima entre los valores y la semántica del movimiento (aunque el documento intenta explicar eso también):
Si en ese momento
swap
se presentó así:Entonces la gente habría mirado eso y dicho:
El punto principal:
Tal como estaba, usando
move
, nadie preguntó:A medida que pasaron los años y la propuesta fue refinada, las nociones de lvalue y rvalue se refinaron en las categorías de valor que tenemos hoy:
(imagen robada descaradamente de dirkgently )
Y así, hoy, si quisiéramos
swap
decir con precisión lo que está haciendo, en lugar de por qué , debería verse más como:Y la pregunta que todos deberían hacerse es si el código anterior es más o menos legible que:
O incluso el original:
En cualquier caso, el programador de C ++ de Journeyman debe saber que, bajo el capó
move
, no está pasando nada más que un elenco. Y el programador principiante de C ++, al menos conmove
, será informado de que la intención es moverse de la rhs, en lugar de copiar de la rhs, incluso si no entienden exactamente cómo se logra eso.Además, si un programador desea esta funcionalidad con otro nombre,
std::move
no posee el monopolio de esta funcionalidad y no hay magia de lenguaje no portátil involucrada en su implementación. Por ejemplo, si uno quisiera codificarset_value_category_to_xvalue
, y usarlo en su lugar, es trivial hacerlo:En C ++ 14 se vuelve aún más conciso:
Por lo tanto, si está tan inclinado, decore el
static_cast<T&&>
que mejor le parezca, y tal vez terminará desarrollando una nueva mejor práctica (C ++ está en constante evolución).Entonces, ¿qué hace
move
en términos de código objeto generado?Considera esto
test
:Compilado con
clang++ -std=c++14 test.cpp -O3 -S
, esto produce este código objeto:Ahora si la prueba se cambia a:
No hay absolutamente ningún cambio en absoluto en el código objeto. Uno puede generalizar este resultado a: Para objetos trivialmente móviles ,
std::move
no tiene impacto.Ahora veamos este ejemplo:
Esto genera:
Si se ejecuta
__ZN1XaSERKS_
a través dec++filt
ella produce:X::operator=(X const&)
. No es sorpresa aquí. Ahora si la prueba se cambia a:Entonces todavía no hay ningún cambio en el código objeto generado.
std::move
no ha hecho nada más que emitirj
un valor r, y luego ese valor r seX
une al operador de asignación de copias deX
.Ahora agreguemos un operador de asignación de movimiento a
X
:Ahora el código objeto hace el cambio:
Correr a
__ZN1XaSEOS_
travésc++filt
revela queX::operator=(X&&)
se está llamando en lugar deX::operator=(X const&)
.¡Y eso es todo
std::move
! Desaparece por completo en tiempo de ejecución. Su único impacto es en tiempo de compilación, donde podría alterar la sobrecarga que se llama.fuente
digraph D { glvalue -> { lvalue; xvalue } rvalue -> { xvalue; prvalue } expression -> { glvalue; rvalue } }
para el bien público :) Descárguelo aquí como SVGallow_move
;)movable
.std::move
arvalue_cast
: youtube.com/…rvalue_cast
es ambiguo en su significado: ¿qué tipo de rvalue devuelve?xvalue_cast
Sería un nombre consistente aquí. Desafortunadamente, la mayoría de las personas, en este momento, tampoco entenderían lo que está haciendo. En unos años más, espero que mi declaración se vuelva falsa.Permítanme dejar aquí una cita de las preguntas frecuentes de C ++ 11 escritas por B. Stroustrup, que es una respuesta directa a la pregunta de OP:
Por cierto, realmente disfruté las preguntas frecuentes: vale la pena leerlo.
fuente