C++ valor


Ejemplo

Una expresión de rvalor es cualquier expresión a la que se puede mover implícitamente, independientemente de si tiene identidad.

Más precisamente, las expresiones rvalue se pueden usar como argumento para una función que toma un parámetro de tipo T && (donde T es el tipo de expr ). Solo se pueden dar expresiones de valor como argumentos a tales parámetros de función; Si se usa una expresión sin valor, la resolución de sobrecarga seleccionará cualquier función que no use un parámetro de referencia de valor. Y si no existe ninguno, entonces obtienes un error.

La categoría de expresiones rvalue incluye todas las expresiones xvalue y prvalue, y solo esas expresiones.

La función de biblioteca estándar std::move existe para transformar explícitamente una expresión sin valor en un valor. Más específicamente, convierte la expresión en un xvalor, ya que incluso si antes era una expresión de prvalor sin identidad, al pasarla como parámetro a std::move , gana identidad (el nombre del parámetro de la función) y se convierte en un xvalor.

Considera lo siguiente:

std::string str("init");                       //1
std::string test1(str);                        //2
std::string test2(std::move(str));             //3

str = std::string("new value");                //4 
std::string &&str_ref = std::move(str);        //5
std::string test3(str_ref);                    //6

std::string tiene un constructor que toma un solo parámetro de tipo std::string&& , comúnmente llamado "constructor de movimiento". Sin embargo, la categoría de valor de la expresión str no es un rvalue (específicamente es un lvalue), por lo que no puede llamar a esa sobrecarga del constructor. En su lugar, llama a const std::string& overload, el constructor de copia.

La línea 3 cambia las cosas. El valor de retorno de std::move es un T&& , donde T es el tipo base del parámetro pasado. Así que std::move(str) devuelve std::string&& . Una llamada de función cuyo valor de retorno es una referencia rvalue es una expresión rvalue (específicamente un valor x), por lo que puede llamar al constructor de movimiento de std::string . Después de la línea 3, str se ha movido desde (los contenidos de quién ahora están indefinidos).

La línea 4 pasa un operador temporal al operador de asignación de std::string . Esto tiene una sobrecarga que lleva un std::string&& . La expresión std::string("new value") es una expresión rvalue (específicamente un prvalue), por lo que puede llamar a esa sobrecarga. Por lo tanto, el temporal se mueve a str , reemplazando los contenidos indefinidos con contenidos específicos.

La línea 5 crea una referencia str_ref llamada str_ref que se refiere a str . Aquí es donde las categorías de valor se vuelven confusas.

Vea, mientras que str_ref es una referencia de rvalue a std::string , la categoría de valor de la expresión str_ref no es un valor de r . Es una expresión lvalue. Sí, en serio. Debido a esto, no se puede llamar al constructor de movimiento de std::string con la expresión str_ref . La línea 6, por lo tanto, copia el valor de str en test3 .

Para moverlo, tendríamos que emplear std::move nuevamente.