Copy-initialization
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
;
|
(5) | ||||||||
T
arreglo
[
N
] = {
secuencia-otro
};
|
(6) | ||||||||
Explicación
La inicialización por copia se realiza en las siguientes situaciones:
T
se declara con el inicializador que consiste en un signo igual seguido de una expresión.
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).
Los efectos de la inicialización por copia son:
|
(desde C++17) |
-
Primero
(hasta C++17)
En caso contrario
(desde C++17)
, si
Tes un tipo clase y la versión sin calificadores cv del tipo de other esTo una clase derivada deT, se examinan los constructores no explícitos deTy se selecciona la mejor coincidencia mediante resolución de sobrecarga. Ese constructor se llama entonces para inicializar el objeto.
-
De lo contrario, si
Tes un tipo clase, y la versión sin calificadores cv del tipo de other no esTo derivado deT, o siTes 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 aT(o a un tipo derivado deTsiTes 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 deTsi 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
Tni 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 deT.
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 |