Un costruttore move di classe T è un costruttore non-template il cui primo parametro è T&&, const T&&, volatile T&&, o const volatile T&&, e non ci sono altri parametri, o il resto dei parametri ha tutti valori predefiniti.
Contenuti
- 1 Sintassi
- 2 Spiegazione
- 3 Costruttore di move implicitamente dichiarato
- 4 Costruttore di move implicitamente dichiarato cancellato
- 5 Costruttore di move banale
- 6 Costruttore di move ammissibile
- 7 Costruttore di move implicitamentedefinito implicitamente
- 8 Note
- 9 Esempio
- 10 Segnalazioni di difetti
- 11 Vedi anche
Sintassi
class_name ( class_name && ) |
(1) | (dal C++11) | |||||||
class_name ( class_name && ) = default; |
(2) | (dal C++11) | |||||||
class_name ( class_name && ) = delete; |
(3) | (dal C++11) | |||||||
Dove class_name deve nominare la classe corrente (o l’istanza corrente di un modello di classe), o, quando è dichiarato nello spazio dei nomi o in una dichiarazione amica, deve essere un nome di classe qualificato.
Spiegazione
Il costruttore move è tipicamente chiamato quando un oggetto è inizializzato (tramite inizializzazione diretta o copia-inizializzazione) da rvalue (xvalue o prvalue) (fino al C++17)xvalue (dal C++17) dello stesso tipo, inclusa
- inizializzazione: T a = std::move(b); o T a(std::move(b));, dove
bè di tipoT; - passaggio di argomento di funzione: f(std::move(a));, dove
aè di tipoTefè void f(T t); - ritorno di funzione: return a; dentro una funzione come T f(), dove
aè di tipoTche ha un costruttore move.
Quando l’inizializzatore è un valore pr, la chiamata al costruttore move è spesso ottimizzata (fino al C++17) e mai fatta (dal C++17), vedi copy elision.
I costruttori move tipicamente “rubano” le risorse detenute dall’argomento (es.) piuttosto che farne delle copie, e lasciano l’argomento in qualche stato valido ma altrimenti indeterminato. Per esempio, spostarsi da una std::string o da uno std::vector può risultare nel lasciare l’argomento vuoto. Tuttavia, non si dovrebbe fare affidamento su questo comportamento. Per alcuni tipi, come std::unique_ptr, lo stato “moved-from” è completamente specificato.
Costruttore di mosse implicitamente dichiarato
Se non vengono forniti costruttori di mosse definiti dall’utente per un tipo di classe (struct, class, o union), e tutto ciò che segue è vero:
- non ci sono costruttori di copia dichiarati dall’utente;
- non ci sono operatori di assegnazione di copia dichiarati dall’utente;
- non ci sono operatori di assegnazione di spostamento dichiarati dall’utente;
- non c’è un distruttore dichiarato dall’utente.
allora il compilatore dichiarerà un costruttore move come un membro non esplicito inline public della sua classe con la firma T::T(T&&).
Una classe può avere più costruttori di mosse, per esempio sia T::T(const T&&) che T::T(T&&). Se alcuni costruttori di mosse definiti dall’utente sono presenti, l’utente può ancora forzare la generazione del costruttore di mosse implicitamente dichiarato con la parola chiave default.
Il costruttore di mosse dichiarato implicitamente (o predefinito alla sua prima dichiarazione) ha una specifica di eccezione come descritto nella specifica di eccezione dinamica (fino a C++17)specifica di eccezione (da C++17)
Cancellato costruttore di mosse dichiarato implicitamente
Il costruttore di mosse dichiarato implicitamente o predefinito per la classe T è definito come cancellato se una delle seguenti è vera:
-
Tha membri di dati non statici che non possono essere spostati (hanno costruttori di spostamento cancellati, inaccessibili o ambigui); -
Tha classe base diretta o virtuale che non può essere spostata (ha costruttori di spostamento cancellati, inaccessibili o ambigui); -
Tha una classe base diretta o virtuale con un distruttore cancellato o inaccessibile; -
Tè una classe simile all’unione e ha un membro variante con un costruttore di spostamento non banale.
Un costruttore move predefinito che viene cancellato viene ignorato dalla risoluzione dei sovraccarichi (altrimenti impedirebbe la copia-inizializzazione da rvalue).
Costruttore di move banale
Il costruttore di move per la classe T è banale se tutto ciò che segue è vero:
- non è fornito dall’utente (cioè, è implicitamente definito o predefinito);
-
Tnon ha funzioni membro virtuali; -
Tnon ha classi base virtuali; - il costruttore di mosse selezionato per ogni base diretta di
Tè banale; - il costruttore di mosse selezionato per ogni membro non statico di tipo classe (o array di tipo classe) di
Tè banale.
Un costruttore di move banale è un costruttore che esegue la stessa azione del costruttore di copia banale, cioè fa una copia della rappresentazione dell’oggetto come per std::memmove. Tutti i tipi di dati compatibili con il linguaggio C (tipi POD) sono banalmente spostabili.
Costruttore di spostamento ammissibile
|
Un costruttore di spostamento è ammissibile se non è cancellato. |
(fino al C++20) |
|
Un costruttore di mosse è ammissibile se
|
(dal C++20) |
La trivialità dei costruttori di mosse ammissibili determina se la classe è un tipo a vita implicita, e se la classe è un tipo banalmente copiabile.
Costruttore di mossa implicitamente definito
Se il costruttore di mossa implicitamente dichiarato non è né cancellato né banale, è definito (cioè, viene generato e compilato un corpo di funzione) dal compilatore se odr-usato o necessario per la valutazione di una costante. Per i tipi unionali, il costruttore di move implicitamente definito copia la rappresentazione dell’oggetto (come da std::memmove). Per i tipi di classe non unionali (class e struct), il costruttore move esegue lo spostamento completo dei membri delle basi e dei membri non statici dell’oggetto, nel loro ordine di inizializzazione, usando l’inizializzazione diretta con un argomento xvalue. Se questo soddisfa i requisiti di un costruttore constexpr, il costruttore move generato è constexpr.
Note
Per rendere possibile la garanzia di eccezione forte, i costruttori move definiti dall’utente non dovrebbero lanciare eccezioni. Per esempio, std::vector si basa su std::move_if_noexcept per scegliere tra move e copy quando gli elementi devono essere riposizionati.
Se sono forniti entrambi i costruttori copy e move e nessun altro costruttore è praticabile, la risoluzione dei sovraccarichi seleziona il costruttore move se l’argomento è un rvalore dello stesso tipo (un xvalore come il risultato di std::move o un prvalore come un temporaneo senza nome (fino al C++17)), e seleziona il costruttore copy se l’argomento è un lvalue (un oggetto con nome o una funzione/operatore che restituisce un riferimento a lvalue). Se viene fornito solo il costruttore di copia, tutte le categorie di argomenti lo selezionano (finché prende un riferimento a const, poiché gli rvalori possono legarsi a riferimenti const), il che rende la copia il fallback per lo spostamento, quando lo spostamento non è disponibile.
Un costruttore è chiamato un ‘costruttore di spostamento’ quando prende un riferimento rvalue come parametro. Non è obbligato a spostare nulla, la classe non è obbligata ad avere una risorsa da spostare e un ‘costruttore di spostamento’ potrebbe non essere in grado di spostare una risorsa come nel caso ammissibile (ma forse non sensato) in cui il parametro è un riferimento rvalue const (const T&&).
Esempio
#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);}
Output:
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
Segnalazioni di difetti
Le seguenti segnalazioni di difetti che cambiano il comportamento sono state applicate retroattivamente agli standard C++ precedentemente pubblicati.
| DR | Applicato a | Comportamento come pubblicato | Comportamento corretto |
|---|---|---|---|
| CWG 1402 | C++11 | un costruttore di movimento predefinito che avrebbe chiamato un costruttore di copia non banale è stato eliminato; un costruttore di mosse predefinito che è stato eliminato ha comunque partecipato alla risoluzione dei sovraccarichi |
consente la chiamata a tale costruttore di copie; fatta ignorare nella risoluzione degli overload |
| CWG 2094 | C++11 | un sottooggetto volatile fatto di un costruttore di move predefinito nonbanale (CWG496) |
la banalità non è interessata |
Vedi anche
- costruttore di conversione
- assegnazione di copia
- costruttore di copia
- elisione di copia
- costruttore predefinito
- distruttore
- esplicito
- inizializzazione
- inizializzazione aggregata
- inizializzazione costante
- inizializzazione di copia
- inizializzazione predefinita
- inizializzazione diretta
- inizializzatore lista
- inizializzazione lista
- inizializzazione riferimento
- inizializzazione valore
- inizializzazione zero
- assegnazione spostamento
- nuovo