Namespaces
Variants

Copy-initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Inicializa un objeto desde otro objeto.

Contenidos

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 arreglo [ N ] = { secuencia-otro }; (6)

Explicación

La inicialización por copia se realiza en las siguientes situaciones:

1) Cuando una variable nombrada (automática, estática o local de hilo) de un tipo no referencia T se declara con el inicializador que consiste en un signo igual seguido de una expresión.
2) (until C++11) Cuando una variable nombrada de tipo escalar T se declara con el inicializador consistente en un signo igual seguido de una expresión entre llaves (Nota: a partir de C++11, esto se clasifica como list initialization , y no se permite la conversión que reduzca precisión).
3) Al pasar un argumento a una función por valor.
4) Al retornar desde una función que devuelve por valor.
5) Al lanzar o capturar una excepción por valor.
6) Como parte de la aggregate initialization , para inicializar cada elemento para el cual se proporciona un inicializador.

Los efectos de la inicialización por copia son:

  • Primero, si T es un tipo de clase y el inicializador es una expresión prvalue cuyo tipo sin calificadores cv es la misma clase que T , la propia expresión de inicialización, en lugar de un temporal materializado a partir de ella, se utiliza para inicializar el objeto destino: ver copy elision .
(desde C++17)
  • Primero (hasta C++17) En caso contrario (desde C++17) , si T es un tipo clase y la versión sin calificadores cv del tipo de other es T o una clase derivada de T , se examinan los constructores no explícitos de T y se selecciona la mejor coincidencia mediante resolución de sobrecarga. Ese constructor se llama entonces para inicializar el objeto.
  • De lo contrario, si T es un tipo clase, y la versión sin calificadores cv del tipo de other no es T o derivado de T , o si T es un tipo no-clase, pero el tipo de other es un tipo clase, secuencias de conversión definidas por el usuario que pueden convertir desde el tipo de other a T (o a un tipo derivado de T si T es un tipo clase y está disponible una función de conversión) son examinadas y la mejor es seleccionada mediante resolución de sobrecarga. El resultado de la conversión, que es un temporal rvalue (hasta C++11) temporal prvalue (desde C++11) (hasta C++17) expresión prvalue (desde C++17) de la versión sin calificadores cv de T si se usó un constructor de conversión , es entonces usado para inicializar directamente el objeto. El último paso es usualmente optimizado fuera y el resultado de la conversión es construido directamente en la memoria asignada para el objeto destino, pero el constructor apropiado (move o copy) debe ser accesible aunque no sea usado. (hasta C++17)
  • En caso contrario (si ni T ni el tipo de other son tipos clase), se utilizan conversiones estándar , si es necesario, para convertir el valor de other a la versión sin calificadores cv de T .

Notas

La inicialización por copia es menos permisiva que la inicialización directa: explicit constructors no son converting constructors y no se consideran para la inicialización por copia.

struct Exp { explicit Exp(const char*) {} }; // no convertible desde const char*
Exp e1("abc");  // CORRECTO
Exp e2 = "abc"; // Error, la inicialización por copia no considera el constructor explícito
struct Imp { Imp(const char*) {} }; // convertible desde const char*
Imp i1("abc");  // CORRECTO
Imp i2 = "abc"; // CORRECTO

Además, la conversión implícita en la inicialización por copia debe producir T directamente desde el inicializador, mientras que, por ejemplo, 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 desde std::string
S s("abc");   // OK: conversión desde const char[4] a std::string
S s = "abc";  // Error: no hay conversión desde const char[4] a S
S s = "abc"s; // OK: conversión desde std::string a S

Si other es una expresión rvalue, un move constructor será seleccionado por resolución de sobrecarga y llamado durante la inicialización por copia. Esto todavía se considera inicialización por copia; no existe un término especial (por ejemplo, move-initialization) para este caso.

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

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

Ejemplo

#include <memory>
#include <string>
#include <utility>
struct A
{
    operator int() { return 12;}
};
struct B
{
    B(int) {}
};
int main()
{
    std::string s = "test";        // OK: el constructor no es explícito
    std::string s2 = std::move(s); // esta inicialización por copia realiza un movimiento
//  std::unique_ptr<int> p = new int(1); // error: el constructor es explícito
    std::unique_ptr<int> p(new int(1));  // OK: inicialización directa
    int n = 3.14;    // conversión flotante-integral
    const int b = n; // const no importa
    int c = b;       // ...de cualquier manera
    A a;
    B b0 = 12;
//  B b1 = a;       // < error: conversión de 'A' a tipo no escalar 'B' solicitada
    B b2{a};        // < idéntico, llama A::operator int(), luego B::B(int)
    B b3 = {a};     // <
    auto b4 = B{a}; // <
//  b0 = a;         // < error, se necesita operador de asignación sobrecargado
    [](...){}(c, b0, b3, b4); // simular que estas variables se utilizan
}

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 5 C++98 la calificación cv del tipo destino se aplicaba al
temporal inicializado por un constructor de conversión
el temporal no está calificado cv
CWG 177 C++98 la categoría de valor del temporal creado durante
la inicialización por copia de un objeto de clase no estaba especificada
especificado como rvalue

Véase también