Copy assignment operator
Un operador de asignación de copia es una función miembro no estática no plantilla con el nombre operator = que puede ser llamado con un argumento del mismo tipo de clase y copia el contenido del argumento sin modificar el argumento.
Sintaxis
Para la sintaxis formal del operador de asignación de copia, consulte declaración de función . La lista de sintaxis a continuación solo demuestra un subconjunto de todas las sintaxis válidas del operador de asignación de copia.
tipo-de-retorno
operator=(
lista-de-parámetros
);
|
(1) | ||||||||
tipo-de-retorno
operator=(
lista-de-parámetros
)
cuerpo-de-función
|
(2) | ||||||||
tipo-de-retorno
operator=(
lista-de-parámetros-sin-valor-predeterminado
) = default;
|
(3) | (desde C++11) | |||||||
tipo-de-retorno
operator=(
lista-de-parámetros
) = delete;
|
(4) | (desde C++11) | |||||||
tipo-de-retorno
nombre-de-clase
::
operator=(
lista-de-parámetros
)
cuerpo-de-función
|
(5) | ||||||||
tipo-de-retorno
nombre-de-clase
::
operator=(
lista-de-parámetros-sin-valor-predeterminado
) = default;
|
(6) | (desde C++11) | |||||||
| class-name | - |
la clase cuyo operador de asignación de copia se está declarando, el tipo de clase se da como
T
en las descripciones siguientes
|
| parameter-list | - |
una
lista de parámetros
de solo un parámetro, que es de tipo
T
,
T&
,
const
T
&
,
volatile
T
&
o
const
volatile
T
&
|
| parameter-list-no-default | - |
una
lista de parámetros
de solo un parámetro, que es de tipo
T
,
T&
,
const
T
&
,
volatile
T
&
o
const
volatile
T
&
y no tiene un argumento predeterminado
|
| function-body | - | el cuerpo de función del operador de asignación de copia |
| return-type | - |
cualquier tipo, pero
T&
es preferido para permitir encadenamiento de asignaciones
|
Explicación
struct X { X& operator=(X& other); // operador de asignación de copia X operator=(X other); // se permite paso por valor // X operator=(const X other); // Error: tipo de parámetro incorrecto }; union Y { // los operadores de asignación de copia pueden tener sintaxis no listadas arriba, // siempre que sigan la sintaxis general de declaración de funciones // y no violen las restricciones enumeradas anteriormente auto operator=(Y& other) -> Y&; // OK: tipo de retorno final Y& operator=(this Y& self, Y& other); // OK: parámetro de objeto explícito // Y& operator=(Y&, int num = 1); // Error: tiene otros parámetros que no son de objeto };
El operador de asignación de copia se llama cuando es seleccionado por la resolución de sobrecarga , por ejemplo, cuando un objeto aparece en el lado izquierdo de una expresión de asignación.
Operador de asignación de copia declarado implícitamente
Si no se proporcionan operadores de asignación de copia definidos por el usuario para un tipo de clase, el compilador siempre declarará uno como un inline public miembro de la clase. Este operador de asignación de copia declarado implícitamente tiene la forma T & T :: operator = ( const T & ) si se cumplen todas las siguientes condiciones:
-
cada base directa
BdeTtiene un operador de asignación de copia cuyos parámetros sonBo const B & o const volatile B & ; -
cada miembro de datos no estático
MdeTde tipo clase o array de tipo clase tiene un operador de asignación de copia cuyos parámetros sonMo const M & o const volatile M & .
De lo contrario, el operador de asignación de copia declarado implícitamente se declara como T & T :: operator = ( T & ) .
Debido a estas reglas, el operador de asignación de copia declarado implícitamente no puede vincularse a un volatile argumento lvalue.
Una clase puede tener múltiples operadores de asignación de copia, por ejemplo, tanto T & T :: operator = ( T & ) como T & T :: operator = ( T ) . Si existen algunos operadores de asignación de copia definidos por el usuario, el usuario aún puede forzar la generación del operador de asignación de copia declarado implícitamente con la palabra clave default . (desde C++11)
El operador de asignación de copia declarado implícitamente (o predeterminado en su primera declaración) tiene una especificación de excepciones como se describe en especificación de excepciones dinámicas (hasta C++17) especificación noexcept (desde C++17)
Debido a que el operador de asignación de copia siempre se declara para cualquier clase, el operador de asignación de la clase base siempre está oculto. Si una using-declaration se utiliza para traer el operador de asignación desde 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 using-declaration también está oculta por la declaración implícita.
Operador de asignación de copia definido implícitamente
Si el operador de asignación de copia declarado implícitamente no está eliminado ni es trivial, se define (es decir, se genera y compila un cuerpo de función) por el compilador si odr-used o needed for constant evaluation (since C++14) . Para tipos union, la asignación de copia definida implícitamente copia la representación del objeto (como mediante std::memmove ). Para tipos class no union, el operador realiza la asignación de copia miembro a miembro de las bases directas y miembros de datos no estáticos del objeto, en su orden de inicialización, usando asignación incorporada para los escalares, asignación de copia miembro a miembro para arrays, y operador de asignación de copia para tipos class (llamado no virtualmente).
|
El operador de asignación de copia definido implícitamente para una clase
|
(desde C++14)
(hasta C++23) |
|
El operador de asignación de copia definido implícitamente para una clase
|
(desde C++23) |
|
La generación del operador de asignación de copia definido implícitamente está obsoleta si
|
(since C++11) |
Operador de asignación de copia eliminado
Un operador de asignación de copia declarado implícitamente
o explícitamente predeterminado
(since C++11)
para la clase
T
está
indefinido
(until C++11)
definido como eliminado
(since C++11)
si se satisface alguna de las siguientes condiciones:
-
Ttiene un miembro de datos no estático de un tipo no-clase calificado como const (o posiblemente un arreglo multidimensional del mismo). -
Ttiene un miembro de datos no estático de tipo referencia. -
Ttiene un subobjeto potencialmente construido de tipo claseM(o posiblemente un arreglo multidimensional del mismo) tal que la resolución de sobrecarga aplicada para encontrar el operador de asignación de copia deM
-
- no resulta en un candidato utilizable, o
- en el caso de que el subobjeto sea un variant member , selecciona una función no trivial.
|
El operador de asignación de copia declarado implícitamente para la clase
|
(desde C++11) |
Operador de asignación de copia trivial
El operador de asignación de copia para la clase
T
es trivial si se cumplen todas las siguientes condiciones:
- no es proporcionado por el usuario (es decir, está definido implícitamente o por defecto);
-
Tno tiene funciones miembro virtuales; -
Tno tiene clases base virtuales; -
el operador de asignación de copia seleccionado para cada base directa de
Tes trivial; -
el operador de asignación de copia seleccionado para cada miembro de tipo clase (o arreglo de tipo clase) no estático de
Tes trivial.
Un operador de asignación de copia trivial realiza una copia de la representación del objeto como si fuera mediante std::memmove . Todos los tipos de datos compatibles con el lenguaje C (tipos POD) son trivialmente asignables por copia.
Operador de asignación de copia elegible
|
Un operador de asignación de copia es elegible si es declarado por el usuario o si es declarado implícitamente y definible. |
(until C++11) |
|
Un operador de asignación de copia es elegible si no está eliminado. |
(since C++11)
(until C++20) |
|
Un operador de asignación de copia es elegible si se satisfacen todas las siguientes condiciones:
|
(since C++20) |
La trivialidad de los operadores de asignación por copia elegibles determina si la clase es un trivially copyable type .
Notas
Si se proporcionan tanto el operador de asignación de copia como el de movimiento, la resolución de sobrecarga selecciona la asignación de movimiento si el argumento es un rvalue (ya sea un prvalue como un temporal sin nombre o un xvalue como el resultado de std::move ), y selecciona la asignación de copia si el argumento es un lvalue (objeto con nombre o una función/operador que devuelve una referencia lvalue). Si solo se proporciona la asignación de copia, todas las categorías de argumentos la seleccionan (siempre que tome su argumento por valor o como referencia a const, ya que los rvalues pueden enlazarse a referencias const), lo que convierte a la asignación de copia en el respaldo para la asignación de movimiento cuando esta no está disponible.
No está especificado si los subobjetos de clase base virtual que son accesibles a través de más de una ruta en el esquema de herencia, son asignados más de una vez por el operador de asignación de copia definido implícitamente (lo mismo aplica para move assignment ).
Consulte sobrecarga del operador de asignación para obtener detalles adicionales sobre el comportamiento esperado de un operador de asignación de copia definido por el usuario.
Ejemplo
#include <algorithm> #include <iostream> #include <memory> #include <string> struct A { int n; std::string s1; A() = default; A(A const&) = default; // asignación de copia definida por el usuario (idioma copy-and-swap) A& operator=(A other) { std::cout << "copy assignment of A\n"; std::swap(n, other.n); std::swap(s1, other.s1); return *this; } }; struct B : A { std::string s2; // asignación de copia definida implícitamente }; struct C { std::unique_ptr<int[]> data; std::size_t size; // asignación de copia definida por el usuario (no es el idioma copy-and-swap) // nota: copy-and-swap siempre reasignaría recursos C& operator=(const C& other) { if (this != &other) // no es una autoasignación { if (size != other.size) // el recurso no puede reutilizarse { data.reset(new int[other.size]); size = other.size; } std::copy(&other.data[0], &other.data[0] + size, &data[0]); } return *this; } }; int main() { A a1, a2; std::cout << "a1 = a2 calls "; a1 = a2; // asignación de copia definida por el usuario B b1, b2; b2.s1 = "foo"; b2.s2 = "bar"; std::cout << "b1 = b2 calls "; b1 = b2; // asignación de copia definida implícitamente std::cout << "b1.s1 = " << b1.s1 << "; b1.s2 = " << b1.s2 << '\n'; }
Salida:
a1 = a2 calls copy assignment of A b1 = b2 calls copy assignment of A b1.s1 = foo; b1.s2 = bar
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 1353 | C++98 |
las condiciones donde los operadores de asignación de copia declarados implícitamente
eran indefinidos no consideraban tipos de arreglos multidimensionales |
considerar estos tipos |
| CWG 2094 | C++11 |
un subobjeto volatile hacía que los operadores de asignación de copia predeterminados
fueran no triviales ( CWG issue 496 ) |
trivialidad no afectada |
| CWG 2171 | C++11 | operator = ( X & ) = default era no trivial | hecho trivial |
| CWG 2180 | C++11 |
un operador de asignación de copia predeterminado para la clase
T
no se definía como eliminado
si
T
es abstracta y tiene clases base virtuales directas no asignables por copia
|
el operador se define
como eliminado en este caso |
| CWG 2595 | C++20 |
un operador de asignación de copia no era elegible si existe
otro operador de asignación de copia que está más restringido pero no satisface sus restricciones asociadas |
puede ser elegible
en este caso |