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

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 ;

catch ( T objet )

(5)
T tableau [ N ] = { séquence-autre }; (6)

Explication

L'initialisation par copie est effectuée dans les situations suivantes :

1) Lorsqu'une variable nommée (automatique, statique ou locale au thread) d'un type non-référence T est déclarée avec l'initialiseur constitué d'un signe égal suivi d'une expression.
2) (jusqu'à C++11) Lorsqu'une variable nommée de type scalaire 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).
3) Lors du passage d'un argument à une fonction par valeur.
4) Lors du retour d'une fonction qui retourne par valeur.
5) Lors du throw ou de la catch d'une exception par valeur.
6) Dans le cadre de l' initialisation d'agrégat , pour initialiser chaque élément pour lequel un initialiseur est fourni.

Les effets de la copy-initialization sont :

  • Premièrement, si T est un type classe et que l'initialiseur est une expression prvalue dont le type non qualifié cv est la même classe que T , l'expression d'initialisation elle-même, plutôt qu'un temporaire matérialisé à partir de celle-ci, est utilisée pour initialiser l'objet de destination : voir copy elision .
(depuis C++17)
  • Premièrement (jusqu'au C++17) Sinon (depuis C++17) , si T est un type classe et que la version non qualifiée cv du type de other est T ou une classe dérivée de T , les constructeurs non-explicites de T sont 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 T est un type classe, et que la version sans cv-qualificateur du type de other n'est pas T ou dérivé de T , ou si T est 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 vers T (ou vers un type dérivé de T si T est 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 de T si 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 T ni 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 de T .

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

Voir aussi