Copy-initialization
Initialise un objet à partir d'un autre objet.
Table des matières |
Syntaxe
T
objet
=
autre
;
|
(1) | ||||||||
T
objet
=
{
autre
};
|
(2) | (jusqu'à C++11) | |||||||
f
(
autre
)
|
(3) | ||||||||
return
autre
;
|
(4) | ||||||||
throw
objet
;
|
(5) | ||||||||
T
tableau
[
N
] = {
séquence-autre
};
|
(6) | ||||||||
Explication
L'initialisation par copie est effectuée dans les situations suivantes :
T
est déclarée avec l'initialiseur constitué d'un signe égal suivi d'une expression.
T
est déclarée avec l'initialiseur constitué d'un signe égal suivi d'une expression entre accolades (Note : à partir de C++11, ceci est classé comme
initialisation de liste
, et la conversion rétrécissante n'est pas autorisée).
Les effets de la copy-initialization sont :
|
(depuis C++17) |
-
Premièrement
(jusqu'au C++17)
Sinon
(depuis C++17)
, si
Test un type classe et que la version non qualifiée cv du type de other estTou une classe dérivée deT, les constructeurs non-explicites deTsont examinés et la meilleure correspondance est sélectionnée par résolution de surcharge. Ce constructeur est ensuite appelé pour initialiser l'objet.
-
Sinon, si
Test un type classe, et que la version sans cv-qualificateur du type de other n'est pasTou dérivé deT, ou siTest un type non-classe, mais que le type de other est un type classe, les séquences de conversion définies par l'utilisateur qui peuvent convertir du type de other versT(ou vers un type dérivé deTsiTest un type classe et qu'une fonction de conversion est disponible) sont examinées et la meilleure est sélectionnée par résolution de surcharge. Le résultat de la conversion, qui est un temporaire rvalue (jusqu'en C++11) temporaire prvalue (depuis C++11) (jusqu'en C++17) expression prvalue (depuis C++17) de la version sans cv-qualificateur deTsi un constructeur de conversion a été utilisé, est ensuite utilisé pour initialiser directement l'objet. La dernière étape est généralement optimisée et le résultat de la conversion est construit directement dans la mémoire allouée pour l'objet cible, mais le constructeur approprié (déplacement ou copie) doit être accessible même s'il n'est pas utilisé. (jusqu'en C++17)
-
Sinon (si ni
Tni le type de other ne sont des types de classe), les conversions standard sont utilisées, si nécessaire, pour convertir la valeur de other en la version non qualifiée cv deT.
Notes
L'initialisation par copie est moins permissive que l'initialisation directe : les constructeurs explicites ne sont pas des constructeurs de conversion et ne sont pas pris en compte pour l'initialisation par copie.
struct Exp { explicit Exp(const char*) {} }; // non convertible depuis const char* Exp e1("abc"); // OK Exp e2 = "abc"; // Erreur, l'initialisation par copie ne considère pas le constructeur explicite struct Imp { Imp(const char*) {} }; // convertible depuis const char* Imp i1("abc"); // OK Imp i2 = "abc"; // OK
De plus, la conversion implicite dans l'initialisation par copie doit produire
T
directement à partir de l'initialiseur, tandis que, par exemple, l'initialisation directe attend une conversion implicite de l'initialiseur vers un argument du constructeur de
T
.
struct S { S(std::string) {} }; // implicitement convertible depuis std::string S s("abc"); // OK : conversion de const char[4] vers std::string S s = "abc"; // Erreur : aucune conversion de const char[4] vers S S s = "abc"s; // OK : conversion de std::string vers S
Si other est une expression rvalue, un move constructor sera sélectionné par la résolution de surcharge et appelé lors de la copy-initialization. Ceci est toujours considéré comme une copy-initialization ; il n'existe pas de terme spécial (par exemple, move-initialization) pour ce cas.
Conversion implicite
est définie en termes d'initialisation par copie : si un objet de type
T
peut être initialisé par copie avec l'expression
E
, alors
E
est implicitement convertible en
T
.
Le signe égal,
=
, dans l'initialisation par copie d'une variable nommée n'est pas lié à l'opérateur d'affectation. Les surcharges de l'opérateur d'affectation n'ont aucun effet sur l'initialisation par copie.
Exemple
#include <memory> #include <string> #include <utility> struct A { operator int() { return 12;} }; struct B { B(int) {} }; int main() { std::string s = "test"; // OK : le constructeur est non-explicite std::string s2 = std::move(s); // cette initialisation par copie effectue un déplacement // std::unique_ptr<int> p = new int(1); // erreur : le constructeur est explicite std::unique_ptr<int> p(new int(1)); // OK : initialisation directe int n = 3.14; // conversion flottant-entier const int b = n; // const n'a pas d'importance int c = b; // ...dans les deux cas A a; B b0 = 12; // B b1 = a; // < erreur : conversion de 'A' vers le type non-scalaire 'B' demandée B b2{a}; // < identique, appel de A::operator int(), puis B::B(int) B b3 = {a}; // < auto b4 = B{a}; // < // b0 = a; // < erreur, opérateur d'affectation surchargé nécessaire [](...){}(c, b0, b3, b4); // simuler l'utilisation de ces variables }
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| CWG 5 | C++98 |
la qualification cv du type de destination est appliquée
au temporaire initialisé par un constructeur de conversion |
le temporaire n'est pas qualifié cv |
| CWG 177 | C++98 |
la catégorie de valeur du temporaire créé durant
la copie-initialisation d'un objet classe n'est pas spécifiée |
spécifiée comme rvalue |