Un constructor de mutare al clasei T
este un constructor non-template al cărui prim parametru este T&&, const T&&, volatile T&& sau const volatile T&& și fie nu există alți parametri, fie restul parametrilor au toți valori implicite.
Contenit
- 1 Sintaxa
- 2 Explicație
- 3 Constructor de mutare declarat implicit
- 4 Constructor de mutare implicit eliminat
- 5 Constructor de mutare trivial
- 6 Constructor de mutare eligibil
- 7 Constructor de mutare implicit
- 7 Constructor de mutare implicit.defined move constructor
- 8 Note
- 9 Exemplu
- 10 Rapoarte de defecte
- 11 Vezi și
Sintaxa
class_name ( class_name && ) |
(1) | (din C++11) | |||||||
class_name ( class_name && ) = implicit; |
(2) | (din C++11) | |||||||
class_name ( class_name && ) = delete; |
(3) | (din C++11) | |||||||
În care nume_clasă trebuie să numească clasa curentă (sau instanțierea curentă a unui șablon de clasă) sau, atunci când este declarată la nivelul spațiului de nume sau într-o declarație friend, trebuie să fie un nume de clasă calificat.
Explicație
Constructorul de mutare este de obicei apelat atunci când un obiect este inițializat (prin inițializare directă sau inițializare prin copiere) din rvaloare (xvaloare sau prvaloare) (până în C++17)xvaloare (din C++17) de același tip, inclusiv
- inițializare: T a = std::move(b); sau T a(std::move(b));, unde
b
este de tipT
; - trecerea argumentelor funcției: f(std::move(a));;, unde
a
este de tipT
șif
este void f(T t); - funcția return: return a; în interiorul unei funcții cum ar fi T f(), unde
a
este de tipT
care are un constructor move.
Când inițializatorul este o prvaloare, apelul constructorului de mutare este adesea optimizat (până în C++17)nu se face niciodată (din C++17), vezi eliziune de copiere.
Constructorii de mutare „fură” de obicei resursele deținute de argument (de ex.g. pointeri la obiecte alocate dinamic, descriptori de fișiere, socket-uri TCP, fluxuri I/O, fire de execuție etc.) mai degrabă decât să facă copii ale acestora, și lasă argumentul într-o stare validă, dar altfel nedeterminată. De exemplu, trecerea de la un std::string sau de la un std::vector poate duce la lăsarea argumentului gol. Cu toate acestea, nu trebuie să vă bazați pe acest comportament. Pentru unele tipuri, cum ar fi std::unique_ptr, starea „moved-from” este complet specificată.
Constructor de mutare declarat implicit
Dacă nu sunt prevăzuți constructori de mutare definiți de utilizator pentru un tip de clasă (struct, class sau union) și toate următoarele sunt adevărate:
- nu există constructori de copiere declarați de utilizator;
- nu există operatori de atribuire de copiere declarați de utilizator;
- nu există operatori de atribuire de mutare declarați de utilizator;
- nu există un destructor declarat de utilizator.
atunci compilatorul va declara un constructor de mutare ca membru neexplicit inline public
al clasei sale cu semnătura T::T(T&&)
.
O clasă poate avea mai mulți constructori de mutare, de exemplu, atât T::T(const T&&), cât și T::T(T&&). În cazul în care există constructori de mutare definiți de utilizator, utilizatorul poate totuși să forțeze generarea constructorului de mutare implicit declarat cu ajutorul cuvântului cheie default
.
Constructorul de mutare declarat implicit (sau implicit la prima sa declarare) are o specificație de excepție așa cum este descrisă în specificația de excepție dinamică (până în C++17)specificația de excepție (din C++17)
Constructor de mutare implicit declarat șters
Constructorul de mutare implicit declarat sau implicit pentru clasa T
este definit ca fiind șters dacă oricare dintre următoarele este adevărat:
-
T
are membri de date non-statice care nu pot fi mutați (au constructori de mutare șterși, inaccesibili sau ambigui); -
T
are o clasă de bază directă sau virtuală care nu poate fi mutată (are constructori de mutare șterși, inaccesibili sau ambigui); -
T
are o clasă de bază directă sau virtuală cu un destructor șters sau inaccesibil; -
T
este o clasă de tip uniune și are un membru variant cu un constructor de mutare netrivial.
Un constructor de mutare predefinit care este eliminat este ignorat de rezoluția supraîncărcării (altfel ar împiedica inițializarea prin copiere din rvaloare).
Constructorul de mutare trivial
Constructorul de mutare pentru clasa T
este trivial dacă toate elementele următoare sunt adevărate:
- nu este furnizat de utilizator (ceea ce înseamnă că este definit implicit sau implicit);
-
T
nu are funcții membre virtuale; -
T
nu are clase de bază virtuale; - constructorul de mutare selectat pentru fiecare bază directă a lui
T
este trivial; - constructorul de mutare selectat pentru fiecare tip de clasă non-statică (sau matrice de tip de clasă) membru al lui
T
este trivial.
Un constructor trivial de mutare este un constructor care efectuează aceeași acțiune ca și constructorul trivial de copiere, adică realizează o copie a reprezentării obiectului ca și cum ar fi făcut-o prin std::memmove. Toate tipurile de date compatibile cu limbajul C (tipuri POD) sunt mutabile trivial.
Constructor de mutare eligibil
Un constructor de mutare este eligibil dacă nu este șters. |
(până la C++20) |
Un constructor de mutare este eligibil dacă
|
(din C++20) |
Trivialitatea constructorilor de mutare eligibili determină dacă clasa este un tip cu durată de viață implicită și dacă clasa este un tip copiabil în mod trivial.
Constructor de mutare definit implicit
Dacă constructorul de mutare declarat implicit nu este nici eliminat, nici trivial, acesta este definit (adică se generează și se compilează un corp de funcție) de compilator dacă este odr-utilizat sau este necesar pentru evaluarea constantelor. Pentru tipurile de uniune, constructorul de mutare implicit-definit copiază reprezentarea obiectului (ca prin std::memmove). Pentru tipurile de clasă non-union (class și struct), constructorul move efectuează mutarea completă a bazelor și a membrilor non-statici ai obiectului, în ordinea inițializării lor, utilizând inițializarea directă cu un argument xvalue. Dacă acest lucru satisface cerințele unui constructor constexpr, constructorul de mutare generat este constexpr
.
Note
Pentru a face posibilă garantarea excepțiilor puternice, constructorii de mutare definiți de utilizator nu trebuie să arunce excepții. De exemplu, std::vector se bazează pe std::move_if_noexcept pentru a alege între move și copy atunci când elementele trebuie să fie relocate.
Dacă sunt furnizați atât constructorii copy cât și move și niciun alt constructor nu este viabil, rezoluția supraîncărcărilor selectează constructorul move dacă argumentul este o valoare r de același tip (o valoare x, cum ar fi rezultatul std::move sau o valoare pr, cum ar fi o valoare temporară fără nume (până în C++17)) și selectează constructorul copy dacă argumentul este o valoare l (un obiect cu nume sau o funcție/operator care returnează o referință la o valoare l). Dacă este furnizat doar constructorul de copiere, toate categoriile de argumente îl selectează (atâta timp cât acceptă o referință la const, deoarece rvalorile se pot lega la referințe const), ceea ce face din copiere soluția de rezervă pentru mutare, atunci când mutarea nu este disponibilă.
Un constructor se numește „constructor de mutare” atunci când acceptă ca parametru o referință la rvaloare. Nu este obligat să mute nimic, nu este necesar ca clasa să aibă o resursă care să fie mutată, iar un ‘constructor de mutare’ poate să nu fie capabil să mute o resursă ca în cazul permis (dar poate nu sensibil) în care parametrul este o referință const rvalue (const T&&).
Exemplu
#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);}
Scoatere:
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
Rapoarte de defecte
Celelalte rapoarte de defecte care schimbă comportamentul au fost aplicate retroactiv la standardele C++ publicate anterior.
DR | Aplicat la | Comportamentul așa cum a fost publicat | Comportamentul corect |
---|---|---|---|
CWG 1402 | C++11 | a fost eliminat un constructor de mutare implicită care ar apela un constructor de copiere non-trivial; un constructor de mutare prestabilit care este eliminat a participat totuși la rezolvarea supraîncărcării |
permite apelarea unui astfel de constructor de copiere; a fost ignorat în rezolvarea suprasarcinilor |
CWG 2094 | C++11 | un subobiect volatil făcut dintr-un constructor de mișcare defect move constructor nontrivial (CWG496) |
trivialitatea nu este afectată |
Vezi și
- constructor de conversie
- copy assignment
- constructor de copiere
- copy constructor
- eliziune copie
- constructor implicit
- destructor
- explicit
- inițializare
- inițializare agregată
- . inițializare constantă
- inițializare copie
- inițializare implicită
- inițializare directă
- inițializator listă
- inițializare listă
- inițializare referință
- inițializare valoare
- inițializare zero
- mutare atribuire
- nou