Espacios de nombres
Variantes
Acciones

Inicialización de copia

De cppreference.com
< cpp‎ | language

Inicializa un objeto a partir de otro objeto.

Contenido

[editar] Sintaxis

T objeto = otro; (1)
T objeto = {otro} ; (2) (hasta C++11)
f(otro) (3)
return otro; (4)
throw objeto;

catch (T objeto)

(5)
T array[N] = {otro}; (6)

[editar] Explicación

La inicialización de copia se lleva a cabo en las siguientes situaciones:

1) cuando una variable denominada (automática, estática, o local al hilo) de un tipo no-referencia T se declara con el inicializador que consiste en un signo igual seguido de una expresión;
2) (hasta C++11)cuando una variable denominada de un tipo escalar T se declara con el inicializador que consiste en un signo igual seguido de una expresión entre llaves (observa que a partir de C++11, esto se clasifica como inicialización de lista, y no se permite la conversión de estrechamiento);
3) cuando se pasa un argumento a una función por valor;
4) al retornar de una función que devuelve por valor;
5) al lanzar o atrapar una excepción por valor;
6) como parte de la inicialización de agregado, para inicializar cada elemento para el que se proporciona un inicializador.

Los efectos de la inicialización de copia son:

  • Primero, si T es un tipo clase y el inicializador es una expresión prvalue cuyo tipo no calificado-cv es la misma clase que T, se usa la expresión inicializadora misma, en lugar de un temporal materializado de ella, para inicializar el objeto destino: véase elisión de copia.
(desde C++17)
  • Si T es un tipo clase y la versión no calificada-cv del tipo de otro es T o una clase derivada de T, los constructores no explícitos de T son examinados y la mejor coincidencia se selecciona por la resolución de sobrecarga. Se llama entonces al constructor para inicializar el objeto.
  • Si T es un tipo clase, y la versión no calificada-cv del tipo de otro no es T o derivado de T, o si T es tipo no clase, pero el tipo de otro es un tipo clase, se examinan las secuencias de conversión definidas por el usuario que puedan convertir del tipo de otro a T (o a un tipo derivado de T si T es un tipo clase y se encuentra disponible una función se conversión) y se selecciona la mejor a través de la resolución de sobrecarga, que es un prvalue temporal (hasta C++17)una expresión prvalue (desde C++17) si se utilizó un constructor de conversión, entonces se utiliza para inicializar el objeto mediante la inicialización directa. El último paso habitualmente se optimiza y el resultado de la conversión se construye directamente en la memoria asignada para el objeto destino, pero se requiere que el constructor apropriado (de copia o movimiento) esté accesible aún cuando no se utilice. (hasta C++17)
  • De lo contrario (si ni T ni el tipo de otro son tipos clase), se utilizan las conversiones estándar, si es necesario, para convertir el valor de otro a la versión no calificada-cv de T.

[editar] Notas

La inicialización de copia es menos permisiva que la inicialización directa: los constructores explícitos no son constructores de conversión y no se consideran para la inicialización de copia.

struct Exp { explicit Exp(const char*) {} }; // no convertible de const char*
Exp e1("abc");  // de acuerdo
Exp e2 = "abc"; // ERROR: inicialización de copia no considera
                // al constructor explícito
 
struct Imp { Imp(const char*) {} }; // convertible de const char*
Imp i1("abc");  // de acuerdo
Imp i2 = "abc"; // de acuerdo

Además, la conversión implícita en la inicialización de copia debe producir T directamente del inicializador, mientras que, p. ej., la inicialización directa espera una conversión implícita del inicializador a un argumento del constructor de T.

struct S { S(std::string) {} }; // implícitamente convertible de std::string
S s("abc");   // de acuerdo: conversión de const char[4] a std::string
S s = "abc";  // ERROR: no hay conversión de const char[4] a S
S s = "abc"s; // de acuerdo: conversión de std::string a S

Si otro es una expresión rvalue, se seleccionará el constructor de movimiento por la resolución de sobrecarga y se llamará durante la inicialización de copia. No existe un término tal como inicialización de movimiento.

La conversión implícita se define en términos de la inicialización de copia: si un objeto de tipo T puede inicializarse mediante la inicialización de copia con la expresión E, entonces E es implícitamente convertible a T.

El signo igual, =, en la inicialización de copia de una variable denominada no está relacionado con el operador de asignación. Las sobrecargas del operador de asignación no tienen efecto en la inicialización de copia.

[editar] Ejemplo

#include <string>
#include <utility>
#include <memory>
 
struct A 
{
  operator int() { return 12;}
};
 
struct B 
{
  B(int) {}
};
 
int main()
{
    std::string s = "test"; // de acuerdo: constructor es no explícito
    std::string s2 = std::move(s); // esta inicialización de copia lleva a cabo
                                   // un movimiento
 
//  std::unique_ptr<int> p = new int(1); // ERROR: constructor es explícito
    std::unique_ptr<int> p(new int(1));  // de acuerdo: inicialización directa
 
    int n = 3.14;    // conversión de flotante-entero
    const int b = n; // const no importa
    int c = b;       // ...de ninguna manera
 
 
    A a;
    B b0 = 12;
//    B b1 = a; //< ERROR: se pidió conversión de 'A' a un tipo no escalar 'B' 
    B b2{a};        // < idéntica, llama a A::operator int(), luego a B::B(int)
    B b3 = {a};     // <
    auto b4 = B{a}; // <
 
//    b0 = a; //< ERROR"se necesita sobrecarga del operador de asignación
}


[editar] Véase también