Namespaces
Variants

Copy assignment operator

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

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.

Table des matières

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

1) Déclaration d'un opérateur d'affectation par copie à l'intérieur de la définition de classe.
2-4) Définition d'un opérateur d'affectation de copie à l'intérieur de la définition de classe.
3) L'opérateur d'affectation par copie est explicitement défini par défaut.
4) L'opérateur d'affectation par copie est supprimé.
5,6) Définition d'un opérateur d'affectation par copie en dehors de la définition de classe (la classe doit contenir une déclaration (1) ).
6) L'opérateur d'affectation par copie est explicitement défini par défaut.
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 B de T possède un opérateur d'affectation par copie dont les paramètres sont B ou const B & ou const volatile B & ;
  • chaque membre de données non statique M de T de type classe ou tableau de type classe possède un opérateur d'affectation par copie dont les paramètres sont M ou 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 T est constexpr si

  • T est un literal type , et
  • l'opérateur d'affectation sélectionné pour copier chaque sous-objet de classe de base directe est une fonction constexpr, et
  • pour chaque membre de données non statique de T qui est de type classe (ou tableau de celui-ci), l'opérateur d'affectation sélectionné pour copier ce membre est une fonction constexpr.
(depuis C++14)
(jusqu'à C++23)

L'opérateur d'affectation de copie implicitement défini pour une classe T est constexpr .

(depuis C++23)


La génération de l'opérateur d'affectation de copie implicitement défini est dépréciée si T a un destructeur déclaré par l'utilisateur ou un constructeur de copie déclaré par l'utilisateur.

(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 :

  • T possède un membre de données non statique d'un type non-classe qualifié const (ou éventuellement un tableau multidimensionnel de celui-ci).
  • T possède un membre de données non statique de type référence.
  • T possède un sous-objet potentiellement construit de type classe M (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 de M
  • 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 T est défini comme supprimé si T déclare un constructeur de déplacement ou un opérateur d'affectation de déplacement .

(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) ;
  • T n'a pas de fonctions membres virtuelles ;
  • T n'a pas de classes de base virtuelles ;
  • l'opérateur d'affectation par copie sélectionné pour chaque base directe de T est trivial ;
  • l'opérateur d'affectation par copie sélectionné pour chaque membre de type classe (ou tableau de type classe) non statique de T est 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

Voir aussi