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 tipoT
ef
è void f(T t); - ritorno di funzione: return a; dentro una funzione come T f(), dove
a
è di tipoT
che 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:
-
T
ha membri di dati non statici che non possono essere spostati (hanno costruttori di spostamento cancellati, inaccessibili o ambigui); -
T
ha classe base diretta o virtuale che non può essere spostata (ha costruttori di spostamento cancellati, inaccessibili o ambigui); -
T
ha 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);
-
T
non ha funzioni membro virtuali; -
T
non 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