Copy assignment operator
Un opérateur d'affectation par copie est une fonction membre non-statique non-template avec le nom operator = qui peut être appelé avec un argument du même type de classe et copie le contenu de l'argument sans modifier l'argument.
Syntaxe
Pour la syntaxe formelle de l'opérateur d'affectation de copie, voir déclaration de fonction . La liste de syntaxe ci-dessous ne présente qu'un sous-ensemble de toutes les syntaxes valides d'opérateur d'affectation de copie.
type-retour
operator=(
liste-paramètres
);
|
(1) | ||||||||
type-retour
operator=(
liste-paramètres
)
corps-fonction
|
(2) | ||||||||
type-retour
operator=(
liste-paramètres-sans-défaut
) = default;
|
(3) | (depuis C++11) | |||||||
type-retour
operator=(
liste-paramètres
) = delete;
|
(4) | (depuis C++11) | |||||||
type-retour
nom-classe
::
operator=(
liste-paramètres
)
corps-fonction
|
(5) | ||||||||
type-retour
nom-classe
::
operator=(
liste-paramètres-sans-défaut
) = default;
|
(6) | (depuis C++11) | |||||||
| class-name | - |
la classe dont l'opérateur d'affectation par copie est déclaré, le type de classe est donné comme
T
dans les descriptions ci-dessous
|
| parameter-list | - |
une
liste de paramètres
d'un seul paramètre, qui est de type
T
,
T&
,
const
T
&
,
volatile
T
&
ou
const
volatile
T
&
|
| parameter-list-no-default | - |
une
liste de paramètres
d'un seul paramètre, qui est de type
T
,
T&
,
const
T
&
,
volatile
T
&
ou
const
volatile
T
&
et n'a pas d'argument par défaut
|
| function-body | - | le corps de fonction de l'opérateur d'affectation par copie |
| return-type | - |
tout type, mais
T&
est privilégié pour permettre les affectations en chaîne
|
Explication
struct X { X& operator=(X& other); // opérateur d'affectation par copie X operator=(X other); // passage par valeur autorisé // X operator=(const X other); // Erreur : type de paramètre incorrect }; union Y { // les opérateurs d'affectation par copie peuvent avoir des syntaxes non listées ci-dessus, // tant qu'elles suivent la syntaxe générale de déclaration de fonction // et ne violent pas les restrictions énumérées ci-dessus auto operator=(Y& other) -> Y&; // OK : type de retour suffixé Y& operator=(this Y& self, Y& other); // OK : paramètre objet explicite // Y& operator=(Y&, int num = 1); // Erreur : possède d'autres paramètres non-objets };
L'opérateur d'affectation par copie est appelé chaque fois qu'il est sélectionné par la résolution de surcharge , par exemple lorsqu'un objet apparaît du côté gauche d'une expression d'affectation.
Opérateur d'affectation de copie implicitement déclaré
Si aucun opérateur d'affectation par copie défini par l'utilisateur n'est fourni pour un type de classe, le compilateur en déclarera toujours un comme membre inline public de la classe. Cet opérateur d'affectation par copie implicitement déclaré a la forme T & T :: operator = ( const T & ) si toutes les conditions suivantes sont vraies :
-
chaque base directe
BdeTpossède un opérateur d'affectation par copie dont les paramètres sontBou const B & ou const volatile B & ; -
chaque membre de données non statique
MdeTde type classe ou tableau de type classe possède un opérateur d'affectation par copie dont les paramètres sontMou const M & ou const volatile M & .
Sinon, l'opérateur d'affectation par copie implicitement déclaré est déclaré comme T & T :: operator = ( T & ) .
En raison de ces règles, l'opérateur d'affectation de copie implicitement déclaré ne peut pas se lier à un volatile argument lvalue.
Une classe peut avoir plusieurs opérateurs d'assignation de copie, par exemple à la fois T & T :: operator = ( T & ) et T & T :: operator = ( T ) . Si certains opérateurs d'assignation de copie définis par l'utilisateur sont présents, l'utilisateur peut toujours forcer la génération de l'opérateur d'assignation de copie déclaré implicitement avec le mot-clé default . (depuis C++11)
L'opérateur d'affectation par copie implicitement déclaré (ou par défaut à sa première déclaration) possède une spécification d'exception comme décrit dans spécification d'exception dynamique (jusqu'à C++17) spécification noexcept (depuis C++17)
Parce que l'opérateur d'affectation par copie est toujours déclaré pour toute classe, l'opérateur d'affectation de la classe de base est toujours masqué. Si une using-declaration est utilisée pour importer l'opérateur d'affectation de la classe de base, et que son type d'argument pourrait être le même que le type d'argument de l'opérateur d'affectation implicite de la classe dérivée, la using-declaration est également masquée par la déclaration implicite.
Opérateur d'affectation par copie implicitement défini
Si l'opérateur d'affectation de copie implicitement déclaré n'est ni supprimé ni trivial, il est défini (c'est-à-dire qu'un corps de fonction est généré et compilé) par le compilateur s'il est odr-used ou needed for constant evaluation (depuis C++14) . Pour les types union, l'affectation de copie implicitement définie copie la représentation objet (comme par std::memmove ). Pour les types classe non-union, l'opérateur effectue une affectation de copie membre par membre des bases directes et des membres de données non statiques de l'objet, dans leur ordre d'initialisation, en utilisant l'affectation intégrée pour les scalaires, l'affectation de copie membre par membre pour les tableaux, et l'opérateur d'affectation de copie pour les types classe (appelé non virtuellement).
|
L'opérateur d'affectation de copie implicitement défini pour une classe
|
(depuis C++14)
(jusqu'à C++23) |
|
L'opérateur d'affectation de copie implicitement défini pour une classe
|
(depuis C++23) |
|
La génération de l'opérateur d'affectation de copie implicitement défini est dépréciée si
|
(depuis C++11) |
Opérateur d'assignation de copie supprimé
Un opérateur d'affectation de copie implicitement déclaré
ou explicitement par défaut
(depuis C++11)
pour la classe
T
est
non défini
(jusqu'à C++11)
défini comme supprimé
(depuis C++11)
si l'une des conditions suivantes est satisfaite :
-
Tpossède un membre de données non statique d'un type non-classe qualifié const (ou éventuellement un tableau multidimensionnel de celui-ci). -
Tpossède un membre de données non statique de type référence. -
Tpossède un sous-objet potentiellement construit de type classeM(ou éventuellement un tableau multidimensionnel de celui-ci) tel que la résolution de surcharge appliquée pour trouver l'opérateur d'affectation par copie deM
-
- ne donne pas lieu à un candidat utilisable, ou
- dans le cas où le sous-objet est un variant member , sélectionne une fonction non triviale.
|
L'opérateur d'affectation de copie implicitement déclaré pour la classe
|
(depuis C++11) |
Opérateur d'affectation par copie trivial
L'opérateur d'affectation par copie pour la classe
T
est trivial si toutes les conditions suivantes sont vraies :
- il n'est pas fourni par l'utilisateur (c'est-à-dire qu'il est implicitement défini ou par défaut) ;
-
Tn'a pas de fonctions membres virtuelles ; -
Tn'a pas de classes de base virtuelles ; -
l'opérateur d'affectation par copie sélectionné pour chaque base directe de
Test trivial ; -
l'opérateur d'affectation par copie sélectionné pour chaque membre de type classe (ou tableau de type classe) non statique de
Test trivial.
Un opérateur d'affectation par copie trivial effectue une copie de la représentation de l'objet comme si par std::memmove . Tous les types de données compatibles avec le langage C (types POD) sont trivialement assignables par copie.
Opérateur d'assignation de copie éligible
|
Un opérateur d'assignation de copie est éligible s'il est soit déclaré par l'utilisateur, soit implicitement déclaré et définissable. |
(jusqu'en C++11) |
|
Un opérateur d'assignation de copie est éligible s'il n'est pas supprimé. |
(depuis C++11)
(jusqu'en C++20) |
|
Un opérateur d'assignation de copie est éligible si toutes les conditions suivantes sont satisfaites :
|
(depuis C++20) |
La trivialité des opérateurs d'assignation par copie éligibles détermine si la classe est un type trivialement copiable .
Notes
Si les opérateurs d'affectation par copie et par déplacement sont tous deux fournis, la résolution de surcharge sélectionne l'affectation par déplacement si l'argument est une valeur r (soit une prvalue telle qu'un temporaire sans nom ou une xvalue telle que le résultat de std::move ), et sélectionne l'affectation par copie si l'argument est une lvalue (objet nommé ou fonction/opérateur retournant une référence lvalue). Si seul l'opérateur d'affectation par copie est fourni, toutes les catégories d'arguments le sélectionnent (à condition qu'il prenne son argument par valeur ou par référence à const, car les valeurs r peuvent se lier aux références const), ce qui fait de l'affectation par copie la solution de repli pour l'affectation par déplacement, lorsque le déplacement n'est pas disponible.
Il n'est pas spécifié si les sous-objets de classe de base virtuelle qui sont accessibles par plus d'un chemin dans le réseau d'héritage, sont assignés plus d'une fois par l'opérateur d'assignation de copie implicitement défini (le même s'applique à l'assignation de déplacement ).
Voir la surcharge de l'opérateur d'affectation pour plus de détails sur le comportement attendu d'un opérateur de copie-affectation défini par l'utilisateur.
Exemple
#include <algorithm> #include <iostream> #include <memory> #include <string> struct A { int n; std::string s1; A() = default; A(A const&) = default; // assignation de copie définie par l'utilisateur (idiome copy-and-swap) A& operator=(A other) { std::cout << "copy assignment of A\n"; std::swap(n, other.n); std::swap(s1, other.s1); return *this; } }; struct B : A { std::string s2; // assignation de copie implicitement définie }; struct C { std::unique_ptr<int[]> data; std::size_t size; // assignation de copie définie par l'utilisateur (sans l'idiome copy-and-swap) // note : copy-and-swap réallouerait toujours les ressources C& operator=(const C& other) { if (this != &other) // pas une auto-assignation { if (size != other.size) // la ressource ne peut pas être réutilisée { data.reset(new int[other.size]); size = other.size; } std::copy(&other.data[0], &other.data[0] + size, &data[0]); } return *this; } }; int main() { A a1, a2; std::cout << "a1 = a2 calls "; a1 = a2; // assignation de copie définie par l'utilisateur B b1, b2; b2.s1 = "foo"; b2.s2 = "bar"; std::cout << "b1 = b2 calls "; b1 = b2; // assignation de copie implicitement définie std::cout << "b1.s1 = " << b1.s1 << "; b1.s2 = " << b1.s2 << '\n'; }
Sortie :
a1 = a2 calls copy assignment of A b1 = b2 calls copy assignment of A b1.s1 = foo; b1.s2 = bar
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 | Applicable à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| CWG 1353 | C++98 |
les conditions où les opérateurs d'assignation de copie implicitement déclarés
sont indéfinis ne prenaient pas en compte les types de tableaux multidimensionnels |
prendre en compte ces types |
| CWG 2094 | C++11 |
un sous-objet volatile rendait les opérateurs d'assignation de copie par défaut
non triviaux ( CWG issue 496 ) |
trivialité non affectée |
| CWG 2171 | C++11 | operator = ( X & ) = default était non trivial | rendu trivial |
| CWG 2180 | C++11 |
un opérateur d'assignation de copie par défaut pour la classe
T
n'était pas défini comme supprimé
si
T
est abstraite et a des classes de base virtuelles directes non assignables par copie
|
l'opérateur est défini
comme supprimé dans ce cas |
| CWG 2595 | C++20 |
un opérateur d'assignation de copie n'était pas éligible s'il existe
un autre opérateur d'assignation de copie qui est plus contraint mais ne satisfait pas ses contraintes associées |
il peut être éligible
dans ce cas |