Un constructeur mobile de la classe T
est un constructeur non-modèle dont le premier paramètre est T&&, const T&&, volatile T&&, ou const volatile T&&, et soit il n’y a pas d’autres paramètres, soit le reste des paramètres ont tous des valeurs par défaut.
- Contenu
- Syntaxe
- Explication
- Constructeur de déplacement implicitement déclaré
- Constructeur de déplacement implicitement déclaré supprimé
- Constructeur de déplacement trivial
- Constructeur de déplacement éligible
- Constructeur de déplacement implicitement défini
- Notes
- Exemple
- Rapports de défauts
- Voir aussi
Contenu
- 1 Syntaxe
- 2 Explication
- 3 Constructeur de déplacement implicitement déclaré
- 4 Constructeur de déplacement implicitement déclaré supprimé
- 5 Constructeur de déplacement trivial
- 6 Constructeur de déplacement admissible
- 7 Constructeur de déplacement implicitement-….défini implicitement
- 8 Notes
- 9 Exemple
- 10 Rapports de défauts
- 11 Voir aussi
Syntaxe
nom_classe ( nom_classe && ) |
(1) | (depuis C++11) | |||||||
nom_classe ( nom_classe && ) = défaut ; |
(2) | (depuis C++11) | |||||||
class_name ( class_name && ) = delete ; |
(3) | (depuis C++11) | |||||||
Où class_name doit nommer la classe actuelle (ou l’instanciation actuelle d’un modèle de classe), ou, lorsqu’il est déclaré à la portée de l’espace de nom ou dans une déclaration d’ami, il doit être un nom de classe qualifié.
Explication
Le constructeur move est typiquement appelé lorsqu’un objet est initialisé (par initialisation directe ou par copie-initialisation) à partir de rvalue (xvalue ou prvalue) (jusqu’à C++17)xvalue (depuis C++17) du même type, y compris
- l’initialisation : T a = std::move(b) ; ou T a(std::move(b)) ;, où
b
est de typeT
; - passage d’argument de fonction : f(std: :move(a)) ;, où
a
est de typeT
etf
est void f(T t); - fonction return : return a ; à l’intérieur d’une fonction telle que T f(), où
a
est de typeT
qui possède un constructeur move.
Lorsque l’initialisateur est une prvalue, l’appel du constructeur move est souvent optimisé (jusqu’à C++17)jamais fait (depuis C++17), voir copy elision.
Les constructeurs move « volent » typiquement les ressources détenues par l’argument (par ex.par exemple, des pointeurs vers des objets alloués dynamiquement, des descripteurs de fichiers, des sockets TCP, des flux d’E/S, des threads en cours d’exécution, etc.) plutôt que d’en faire des copies, et laissent l’argument dans un certain état valide mais autrement indéterminé. Par exemple, le déplacement d’un std::string ou d’un std::vector peut avoir pour résultat de laisser l’argument vide. Cependant, il ne faut pas se fier à ce comportement. Pour certains types, tels que std::unique_ptr, l’état déplacé à partir de est entièrement spécifié.
Constructeur de déplacement implicitement déclaré
Si aucun constructeur de déplacement défini par l’utilisateur n’est fourni pour un type de classe (struct, classe ou union), et que tous les éléments suivants sont vrais :
- il n’y a pas de constructeurs de copie déclarés par l’utilisateur;
- il n’y a pas d’opérateurs d’affectation de copie déclarés par l’utilisateur;
- il n’y a pas d’opérateurs d’affectation de déplacement déclarés par l’utilisateur;
- il n’y a pas de destructeur déclaré par l’utilisateur.
alors le compilateur déclarera un constructeur de déplacement comme un membre non explicite inline public
de sa classe avec la signature T::T(T&&)
.
Une classe peut avoir plusieurs constructeurs de déplacement, par exemple à la fois T::T(const T&&) et T::T(T&&). Si certains constructeurs de déplacement définis par l’utilisateur sont présents, l’utilisateur peut toujours forcer la génération du constructeur de déplacement implicitement déclaré avec le mot-clé default
.
Le constructeur de déplacement implicitement déclaré (ou par défaut à sa première déclaration) a une spécification d’exception telle que décrite dans la spécification d’exception dynamique (jusqu’à C++17)spécification d’exception (depuis C++17)
Constructeur de déplacement implicitement déclaré supprimé
Le constructeur de déplacement implicitement déclaré ou par défaut pour la classe T
est défini comme supprimé si l’un des éléments suivants est vrai :
-
T
a des membres de données non statiques qui ne peuvent pas être déplacés (ont des constructeurs de déplacement supprimés, inaccessibles ou ambigus); -
T
a une classe de base directe ou virtuelle qui ne peut pas être déplacée (a des constructeurs de déplacement supprimés, inaccessibles ou ambigus) ; -
T
a une classe de base directe ou virtuelle avec un destructeur supprimé ou inaccessible; -
T
est une classe de type union et a un membre variant avec un constructeur de déplacement non trivial.
Un constructeur de déplacement par défaut qui est supprimé est ignoré par la résolution de surcharge (sinon il empêcherait la copie-initialisation à partir de rvalue).
Constructeur de déplacement trivial
Le constructeur de déplacement pour la classe T
est trivial si tout ce qui suit est vrai :
- il n’est pas fourni par l’utilisateur (ce qui signifie qu’il est défini implicitement ou par défaut);
-
T
n’a pas de fonctions membres virtuelles; -
T
n’a pas de classes de base virtuelles ; - le constructeur de déplacement sélectionné pour chaque base directe de
T
est trivial; - le constructeur de déplacement sélectionné pour chaque membre non statique de type classe (ou tableau de type classe) de
T
est trivial.
Un constructeur de déplacement trivial est un constructeur qui effectue la même action que le constructeur de copie trivial, c’est-à-dire qu’il fait 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 déplaçables.
Constructeur de déplacement éligible
Un constructeur de déplacement est éligible s’il n’est pas supprimé. |
(jusqu’à C++20) |
Un constructeur de déplacement est éligible si
|
(depuis C++20) |
La trivialité des constructeurs de déplacement admissibles détermine si la classe est un type à durée de vie implicite, et si la classe est un type trivialement copiable.
Constructeur de déplacement implicitement défini
Si le constructeur de déplacement 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-utilisé ou nécessaire pour l’évaluation de la constante. Pour les types union, le constructeur de déplacement implicitement défini copie la représentation de l’objet (comme par std::memmove). Pour les types non union (class et struct), le constructeur move effectue un déplacement complet membre par membre des bases et des membres non statiques de l’objet, dans leur ordre d’initialisation, en utilisant une initialisation directe avec un argument xvalue. Si cela satisfait aux exigences d’un constructeur constexpr, le constructeur move généré est constexpr
.
Notes
Pour rendre possible la garantie d’exception forte, les constructeurs move définis par l’utilisateur ne doivent pas lancer d’exceptions. Par exemple, std::vector s’appuie sur std::move_if_noexcept pour choisir entre move et copy lorsque les éléments doivent être relocalisés.
Si les constructeurs copy et move sont tous deux fournis et qu’aucun autre constructeur n’est viable, la résolution de surcharge sélectionne le constructeur move si l’argument est une rvalue du même type (une xvalue telle que le résultat de std::move ou une prvalue telle qu’un temporaire sans nom (jusqu’à C++17)), et sélectionne le constructeur copy si l’argument est une lvalue (objet nommé ou une fonction/opérateur renvoyant une référence lvalue). Si seul le constructeur de copie est fourni, toutes les catégories d’arguments le sélectionnent (tant qu’il prend une référence à const, puisque les rvalues peuvent se lier à des références const), ce qui fait de la copie le fallback pour le déplacement, lorsque le déplacement n’est pas disponible.
Un constructeur est appelé un ‘constructeur de déplacement’ lorsqu’il prend une référence rvalue comme paramètre. Il n’est pas obligé de déplacer quoi que ce soit, la classe n’est pas obligée d’avoir une ressource à déplacer et un ‘constructeur de déplacement’ peut ne pas être capable de déplacer une ressource comme dans le cas admissible (mais peut-être pas sensible) où le paramètre est une référence rvalue const (const T&&).
Exemple
#include <string>#include <iostream>#include <iomanip>#include <utility> struct A{ std::string s; int k; A() : s("test"), k(-1) { } A(const A& o) : s(o.s), k(o.k) { std::cout << "move failed!\n"; } A(A&& o) noexcept : s(std::move(o.s)), // explicit move of a member of class type k(std::exchange(o.k, 0)) // explicit move of a member of non-class type { }}; A f(A a){ return a;} struct B : A{ std::string s2; int n; // implicit move constructor B::(B&&) // calls A's move constructor // calls s2's move constructor // and makes a bitwise copy of n}; struct C : B{ ~C() { } // destructor prevents implicit move constructor C::(C&&)}; struct D : B{ D() { } ~D() { } // destructor would prevent implicit move constructor D::(D&&) D(D&&) = default; // forces a move constructor anyway}; int main(){ std::cout << "Trying to move A\n"; A a1 = f(A()); // return by value move-constructs the target from the function parameter std::cout << "Before move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; A a2 = std::move(a1); // move-constructs from xvalue std::cout << "After move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; std::cout << "Trying to move B\n"; B b1; std::cout << "Before move, b1.s = " << std::quoted(b1.s) << "\n"; B b2 = std::move(b1); // calls implicit move constructor std::cout << "After move, b1.s = " << std::quoted(b1.s) << "\n"; std::cout << "Trying to move C\n"; C c1; C c2 = std::move(c1); // calls copy constructor std::cout << "Trying to move D\n"; D d1; D d2 = std::move(d1);}
Sortie:
Trying to move ABefore move, a1.s = "test" a1.k = -1After move, a1.s = "" a1.k = 0Trying to move BBefore move, b1.s = "test"After move, b1.s = ""Trying to move Cmove failed!Trying to move D
Rapports de défauts
Les rapports de défauts suivants modifiant le comportement ont été appliqués rétroactivement aux normes C++ publiées précédemment.
DR | Appliqué à | Comportement tel que publié | Comportement correct |
---|---|---|---|
CWG 1402 | C++11 | un constructeur de déplacement défectueux qui appellerait un constructeur de copie non trivial a été supprimé ; un constructeur de déplacement par défaut qui est supprimé participe encore à la résolution de surcharge |
permet l’appel à un tel constructeur de copie ; fait ignoré dans la résolution de surcharge |
CWG 2094 | C++11 | un sous-objet volatile fait d’un constructeur de déplacement par défaut non.trivial (CWG496) |
trivialité non affectée |
Voir aussi
- convertir le constructeur
- copier l’affectation
- copier le constructeur.
- élision de copie
- constructeur par défaut
- destructeur
- explicite
- initialisation
- initialisation agrégée
- . initialisation constante
- initialisation par copie
- initialisation par défaut
- initialisation directe
- initialisation liste
- initialisation liste
- initialisation référence
- initialisation valeur
- initialisation zéro
- déplacement affectation
- nouveau
.