Espacios de nombres
Variantes
Acciones

Operador de asignación de movimiento

De cppreference.com
< cpp‎ | language
 
 
 
 

Un operador de asignación de movimiento de la clase T es una función miembro no estática, de no plantilla, con el nombre operator= que toma exactamente un parámetro de tipo T&&, const T&&, volatile T&& o constvolatile T&&.

Contenido

[editar]Sintaxis

nombre_de_clase&nombre_de_clase:: operator= ( nombre_de_clase&& ) (1) (desde C++11)
nombre_de_clase&nombre_de_clase:: operator= ( nombre_de_clase&& ) = default; (2) (desde C++11)
nombre_de_clase&nombre_de_clase:: operator= ( nombre_de_clase&& ) = delete; (3) (desde C++11)

[editar]Explicación

1) Declaración típica de un operador de asignación de movimiento.
2) Forzar al compilador para que genere un operador de asignación de movimiento.
3) Evitar asignación de movimiento implícita.

El operador de asignación de movimiento se llama siempre que se seleccione por la resolución de sobrecarga, p. ej., cuando un objeto aparece en el lado izquierdo de una expresión de asignación, donde el lado derecho es un rvalue del mismo tipo o implícitamente convertible al tipo.

Los operadores de asignación de movimiento típicamente "roban" los recursos albergados por el argumento (p.ej., punteros a objetos asignados dinámicamente, descriptores de archivos, sockets TCP, flujos de E/S, hilos en ejecución, etc.), en lugar de hacer copias de ellos, y dejan el argumento en algún estado válido pero indeterminado. Por ejemplo, asignar mediante movimiento a partir de una cadena (std::string) o de un std::vector puede resultar en que el argumento se quede vacío. Esto, sin embargo, no está garantizado. Una asignación de movimiento es menos, no más restrictivamente definida que una asignación normal; donde la asignación normal tiene que dejar dos copias de los datos una vez que se completa, se requiere que la asignación de movimiento deje solo una.

[editar]Operador de asignación de movimiento declarado implícitamente

Si no se proporcionan operadores de asignación de movimiento definidos por el usuario para un tipo de clase (struct, class, o union), y todo lo siguiente es verdadero:

entonces el compilador declarará un operador de asignación de movimiento como un miembro inline public de su clase con el formato T& T::operator=(T&&).

Una clase puede tener múltiples operadores de asignación de movimiento (p. ej., tanto T& T::operator=(const T&&) como T& T::operator=(T&&)). Si se encuentran presentes algunos operadores de asignación de movimiento definidos por el usuario, el usuario todavía puede forzar la generación del operador de asignación de movimiento declarado implícitamente con la palabra clave default.

El operador de asignación de movimiento declarado implícitamente (o por defecto en su primera declaración) tiene una especificación de excepción como se describe en la especificación de excepción dinámica(hasta C++17)especificación noexcept(desde C++17).

Como siempre se declara algún operador de asignación (de copia o movimiento) para cualquier clase, el operador de asignación de la clase base siempre se oculta. Si se utiliza una declaración using para traer el operador de asignación de la clase base, y su tipo de argumento podría ser el mismo que el tipo de argumento del operador de asignación implícito de la clase derivada, la declaración implícita también oculta la declaración using.

[editar]Operador de asignación de movimiento declarado implícitamente eliminado

El operador de asignación de movimiento declarado implícitamente o por defecto para una clase T se define como eliminado si cualquiera de lo siguiente es verdadero:

  • T tiene un dato miembro no estático que es const;
  • T tiene un dato miembro no estático de un tipo de referencia;
  • T tiene un dato miembro no estático o una clase base directa que no puede asignarse mediante movimiento (tiene un operador de asignación de movimiento eliminado, inaccesible o ambiguo);

Un operador de asignación de movimiento declarado implícitamente eliminado se ignora por la resolución de sobrecarga.

[editar]Operador de asignación de movimiento trivial

El operador de asignación de movimiento para una clase T es trivial si todo lo siguiente es verdadero:

  • no es proporcionado por el usuario (que significa que es definido implícitamente o por defecto);
  • T no tiene funciones miembro virtuales;
  • T no tiene clases base virtuales;
  • el operador de asignación de movimiento seleccionado para cada clase base directa de T es trivial;
  • el operador de asignación de movimiento seleccionado para cada tipo de clase no estático (o tipo de clase de array) miembro de T es trivial;

Un operador de asignación de movimiento trivial realiza la misma acción que el operador de asignación de copia trivial, es decir, hace una copia de la representación del objeto como si se hiciera mediante std::memmove. Todos los tipos de datos compatibles con el lenguaje C (tipos POD) son trivialmente asignables mediante movimiento.

[editar]Operador de asignación de movimiento seleccionable

Un operador de asignación de movimiento es seleccionable si no está eliminado.

(hasta C++20)

Un operador de asignación de movimiento es seleccionable si

  • no está eliminado, y
  • sus restricciones acosiadas, si las tiene, se satisfacen, y
  • no hay un operador de asignación de movimiento con el primer parámetro del mismo tipo y los mismo calificadores cv y de referencia (si tiene) que sea más restrictivo que él.
(desde C++20)

La trivialidad de los operadores de asignación de movimiento seleccionables determina si la clase es un tipo copiable trivialmente.

[editar]Operador de asignación de movimiento definido implícitamente

Si el operador de asignación de movimiento definido implícitamente no es ni eliminado ni trivial, se define (es decir, se genera y se compila un cuerpo de función) por el compilador si hubo uso ODR o es necesario para la evaluación constante(desde C++14).

Para los tipos de union, el operador de asignación de movimiento definido implícitamente copia la representación del objeto (como si fuera mediante std::memmove).

Para los tipos de clase de no-unión (class y struct), el operador de asignación de movimiento realiza una asignación de movimiento miembro por miembro de las bases directas del objeto y de los miembros inmediatos no estáticos, en su orden de declaración, utilizando asignación integrada para los escalares, asignación de movimiento miembro por miembro para arrays, y el operador de asignación de movimiento para los tipos de clase (llamados de manera no virtual).

El operador de asignación de movimiento definido implícitamente para una clase T es constexpr si

  • T es un tipo literal, y
  • el operador de asignación seleccionado para mover cada subobjeto de clase base directa es una función constexpr, y
  • para cada miembro de datos no estático de T que es de tipo clase (o array de los mismos), el operador de asignación seleccionado para mover ese miembro es una función constexpr.
(desde C++14)
(hasta C++23)

El operador de asignación de movimiento definido implícitamente para una clase T es constexpr.

(desde C++23)

Al igual que la asignación de copia, no se especifica si los objetos de la clase base virtual que son accesibles a través de más de una ruta en la red de herencia, se asignan más de una vez por el operador de asignación de movimiento definido implícitamente:

struct V { V& operator=(V&& otro){// puede llamarse una o dos veces// si se llama dos veces, 'otro' es el subobjeto de V del cual se acaba de moverreturn*this;}};   struct A :virtual V {};// operator= llama a V::operator=struct B :virtual V {};// operator= llama a V::operator=struct C : B, A {};// operator= llama a B::operator=, luego a A::operator=// pero solo pueden llamar a V::operator= una vez   int main(){ C c1, c2; c2 = std::move(c1);}

[editar]Notas

Si se proporcionan tanto el operador de copia como el operador de asignación de movimiento, la resolución de sobrecarga selecciona la asignación de movimiento si el argumento es un rvalue (ya sea un prvalue tal como un temporal sin nombre, o un xvalue, tal como el resultado de std::move), y selecciona la asignación de copia si el argumento es un lvalue (un objeto nombrado o una función u operador que devuelve una referencia lvalue). Si solamente se proporciona la asignación de copia, todas las categorías de argumentos la seleccionan (siempre y cuando tome su argumento por valor o como una referencia a const, ya que los rvalues pueden vincularse a referencias const), lo que hace la asignación de copia el respaldo de la asignación de movimiento cuando el movimiento no está disponible.

No se especifica si los objetos de una clase base virtual que son accesibles a través de más de una ruta en la red de herencia, se asignan más de una vez por el operador de asignación de movimiento definido implícitamente (lo mismo aplica a la asignación de copia).

Véase sobrecarga del operador de asignación para mayores detalles del comportamiento esperado de un operador de asignación de movimiento definido por el usuario.

[editar]Ejemplo

#include <string>#include <iostream>#include <utility>   struct A {std::string s;   A(): s("prueba"){}   A(const A& o): s(o.s){std::cout<<"movimiento ha fallado!\n";}   A(A&& o): s(std::move(o.s)){}   A& operator=(const A& otro){ s = otro.s;std::cout<<"asignado por copia\n";return*this;}   A& operator=(A&& otro){ s = std::move(otro.s);std::cout<<"asignado por movimiento\n";return*this;}};   A f(A a){return a;}   struct B : A {std::string s2;int n;// operador de asignación de movimiento implícito B& B::operator=(B&&)// llama al operador de asignación de movimiento de A// llama al operador de asignación de movimiento de s2// y hace una copia bit por bit de n};   struct C : B { ~C(){}// el destructor previene la asignación de movimiento implícita};   struct D : B { D(){} ~D(){}// el destructor prevendría la asignación de movimiento implícita D& operator=(D&&)=default;// de cualquier forma fuerza una asignación de movimiento };   int main(){ A a1, a2;std::cout<<"Intentando mover-asignar a A a partir de un rvalue temporal\n"; a1 = f(A());// asignación por movimiento a partir de un rvalue temporalstd::cout<<"Intentando mover-asignar a A a partir de un xvalue\n"; a2 = std::move(a1);// asignación por movimiento a partir de un xvalue   std::cout<<"\nIntentando mover-asignar a B\n"; B b1, b2;std::cout<<"Antes del movimiento, b1.s = \""<< b1.s<<"\"\n"; b2 = std::move(b1);// llama a la asignación de movimiento implícitastd::cout<<"Posterior al movimiento, b1.s = \""<< b1.s<<"\"\n";   std::cout<<"\nIntentando mover-asignar a C\n"; C c1, c2; c2 = std::move(c1);// llama al operador de asignación de copia   std::cout<<"\nIntentando mover-asignar a D\n"; D d1, d2; d2 = std::move(d1);}

Salida:

Intentando mover-asignar a A a partir de un rvalue temporal asignado por movimiento Intentando mover-asignar a A a partir de un xvalue asignado por movimiento   Intentando mover-asignar a B Antes del movimiento, b1.s = "prueba" asignado por movimiento Posterior al movimiento, b1.s = ""   Intentando mover-asignar a C asignado por copia   Intentando mover-asignar a D asignado por movimiento

[editar]Informes de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 1402 C++11 Un operador de movimiento deducido como por defecto que llamaría
a un operador de asignación de copia no trivial se eliminaba;
un operador de asignación de movimiento deducido como
por defecto que se había definido como eliminado (=delete) todavía
participaba en la resolución de sobrecarga.
Permite llamar a tal operador de
asignación de copia; se hizo que se
ignorara en la resolución de sobrecarga.
CWG 1806 C++11 Faltaba la especificación para un operador de asignación deducido
como por defecto que involucraba a una clase base virtual.
Se añadió.
CWG 2094 C++11 Un subobjeto volatile se deshacía de la trivialidad de un operador
de asignación de movimiento deducido como por defecto (CWG issue 496).
La trivialidad no se afecta.
CWG 2180 C++11 No se definia como eliminado un operador de asignación de
movimiento por defecto para la clase T si T es abstracta y tiene clases
base virtuales directas que no se pueden mover.
En este caso el operador
se define como eliminado.

[editar]Véase también

close