Espacios de nombres
Variantes
Acciones

Operador de asignación de copia

De cppreference.com
< cpp‎ | language
 
 
 
 

Un operador de asignación de copia 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, T&, const T&, volatile T&, o const volatile T&. Para que un tipo sea CopyAssignable, debe tener un miembro operador de asignación de copia público.

Contenido

[editar] Sintaxis

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

[editar] Explicación

1) Declaración típica de un operador de asignación de copia cuando se usa la expresión idiomática de copia e intercambio.
2) Declaración típica de un operador de asignación de copia cuando no se usa la expresión idiomática de copia e intercambio.
3) Forzar a que el operador de asignación de copia se genere por el compilador.
4) Evitar asignación de copia implícita.

El operador de asignación de copia se llama siempre que sea seleccionado por la resolución de sobrecarga, p. ej., cuando un objeto aparece en el lado izquierdo de una expresión de asignación.

[editar] 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 (struct, class, o union), el compilador siempre declarará uno como miembro público en línea de la clase. Este operador de asignación de copia declarado implícitamente tiene el formato T& T::operator=(const T&) si todo lo siguiente es verdadero:

  • cada base directa B de T tiene un operador de asignación de copia cuyos parámetros son B o const B& o const volatile B&;
  • cada miembro de datos no estático M de T de tipo de clase o de tipo de clase de array tiene un operador de asignación de copia cuyos parámetros son M o 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&) (observa que debido a estas reglas, el operador de asignación de copia declarado implícitamente no puede vincularse a un argumento lvalue volatile).

Una clase puede tener múltiples operadores de asignación de copias, p. ej., tanto T& T::operator=(const T&) como T& T::operator=(T). Si algunos operadores de asignación de copia definidos por el usuario están presentes, 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 declarado por defecto en su primera declaración) tiene una especificación de excepción como se describe en especificación de excepción dinámica (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 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 using también está oculta por la declaración implícita.

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

Un operador de asignación de copia declarado implícitamente para la clase T se define como eliminado si se cumple alguna de las siguientes condiciones:

  • T tiene un constructor de movimiento declarado por el usuario;
  • T tiene un operador de asignación de movimiento declarado por el usuario.

De lo contrario, se define como declarado por defecto.

Un operador de asignación de copia declarado por defecto para la clase T se define como eliminado si se cumple alguna de las siguientes condiciones:

  • T tiene un miembro de datos no estático de tipo no clase (o array del mismo) que es const;
  • T tiene un miembro de datos no estático de un tipo de referencia;
  • T tiene un miembro de datos no estático o una clase base directa que no se puede asignar con copia (la resolución de sobrecarga para la asignación de copia falla o selecciona una función eliminada o inaccesible);
  • T es una clase similar a una unión, y tiene un miembro variante cuyo operador de asignación correspondiente no es trivial.
  • El operador de asignación de copia no puede ser elegible.
(desde C++20)

[editar] Operador de asignación de copia trivial

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

  • no se ha proporcionado por el usuario (que significa que está definido implícitamente o declarado por defecto) , y si está declarado por defecto, su signatura es la misma que si hubiera sido definido implícitamente (hasta C++14);
  • T no tiene funciones miembro virtuales;
  • T no tiene clases base virtuales;
  • el operador de asignación de copia seleccionado para cada base directa de T es trivial;
  • el operador de asignación de copia seleccionado para cada tipo de clase no estático (o “array” de tipos de clase') que es miembro de T es trivial;

Un operador de asignación de copia hace una copia de la representación del objeto como si se hiciera por std::memmove. Todos los tipos de datos compatibles con el lenguaje C (tipos POD) son trivialmente asignables por copia.

[editar] Operador de asignación de copia elegible

Un operador de asignación de copia es elegible si es declarado por el usuario o declarado implícitamente y definible.

(hasta C++11)

Un operador de asignación de copia es elegible si no está eliminado.

(desde C++11)
(hasta C++20)

Un operador de asignación de copia es elegible si

  • no está eliminado, y
  • sus restricciones asociadas, si es que las tiene, están satisfechas, y
  • ningún otro operador de asignación de copia con el mismo primer tipo de parámetro y los mismos calificadores-cv/ref (si es que los tiene) está más restringido que él mismo.
(desde C++20)

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

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

Si el operador de asignación de copia declarado implícitamente no se elimina ni es trivial, el compilador lo define (es decir, se genera y compila un cuerpo de función) si hubo uso odr o se necesita para la evaluación constante (desde C++14). Para los tipos de unión (union), la asignación de copia definida implícitamente copia la representación del objeto (como si se copiara mediante std::memmove). Para los tipos de clases de no unión (class y struct), el operador realiza una asignación de copia de las bases del objeto y los miembros no estáticos, en su orden de inicialización, utilizando asignación integrada para los escalares y el operador de asignación de copia para los tipos de clase.

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

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

La generación del operador de asignación de copia definido implícitamente está en desuso si T tiene un destructor declarado por el usuario o un constructor de copia declarado por el usuario.

(desde C++11)

[editar] Notas

Si se proporcionan operadores de asignación de copia y 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 (un objeto con nombre o una función u 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 valores pueden vincularse a referencias const), lo que hace que la asignación de copia sea la alternativa para la asignación de movimiento, cuando el movimiento no está disponible.

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

Véase 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.

[editar] Ejemplo

#include <iostream>
#include <memory>
#include <string>
#include <algorithm>
 
struct A
{
    int n;
    std::string s1;
 
    A() = default;
    A(A const&) = default;
    // asignación de copia definido por el usuario
	// formato copiar-e-intercambiar
    A& operator=(A other)
    {
        std::cout << "asignación de copia de A\n";
        std::swap(n, other.n);
        std::swap(s1, other.s1);
        return *this;
    }
};
 
struct B : A
{
    std::string s2;
    // asignación de copia definido implícitamente
};
 
struct C
{
    std::unique_ptr<int[]> data;
    std::size_t size;
 
    // asignación de copia definida por el usuario (no copiar-e-intercambiar)
    // nota: copiar-e-intercambiar siempre reasignaría recursos
    C& operator=(const C& other)
    {
        // comprueba autoasignación
        if(&other != this)
        {
            // reutiliza almacenamiento cuando es posible
            if(size != other.size)
            {
                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 llama a la ";
    a1 = a2; // asignación de copia definido por el usuario
 
    B b1, b2;
    b2.s1 = "foo";
    b2.s2 = "bar";
    std::cout << "b1 = b2 llama a la ";
    b1 = b2; // asignación de copia definido implícitamente
    std::cout << "b1.s1 = " << b1.s1 << " b1.s2 = " << b1.s2 << '\n';
}

Salida:

a1 = a2 llama a la asignación de copia de A
b1 = b2 llama a la asignación de copia de A
b1.s1 = foo b1.s2 = bar

[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 2094 C++11 Un subobjeto volátil hacía no triviales los operadores de asignación de copia
hechos por defecto (CWG issue 496)
La trivialidad no se afecta
CWG 2171 C++11 operator=(X&) = default era no trivial Se hizo trivial
CWG 2180 C++11 un operador de asiganción de copia por defecto para la clase T no se definía
como eliminado si T es abstracto y tiene clases base virtuales no asignables
por copia
el operador se define
como eliminado en este caso

[editar] Véase también