2013-03-03 16 views
9

Se inibire il costruttore mossa in una classe, non posso più usarlo in un vettore:Perché eliminazione mossa costruttore causa vettore di smettere di lavorare

class Foo 
{ 
    public: 
     Foo(int i) : i_(i) {} 
     Foo(Foo&&) = delete; 

     int i_; 
}; 

int main() 
{ 
    std::vector<Foo> foo; 
    foo.push_back(Foo(1)); 
} 

Perché è così?

+1

Perché push_back richiede che il tipo sia MoveInsertable. Basta leggere i documenti: http://en.cppreference.com/w/cpp/container/vector/push_back –

risposta

34

Sommario

Non eliminare i membri mossa.


Assumendo che il compilatore è completamente C++ 11 conforme, quindi eliminando in modo esplicito il costruttore mossa sarà anche implicitamente dichiarare quanto segue:

Foo(const Foo&) = delete; 
Foo& operator=(const Foo&) = delete; 

cioè se si dichiara un costruttore spostare (o move assignment operator), e non dichiarare membri di copia, sono implicitamente dichiarati come cancellati. Così il vostro Foo classe completo è come se:

class Foo 
{ 
    public: 
     Foo(int i) : i_(i) {} 
     Foo(Foo&&) = delete; 
     Foo(const Foo&) = delete;    // implicitly declared 
     Foo& operator=(const Foo&) = delete; // implicitly declared 

     int i_; 
}; 

Ora vector<Foo>::push_back(Foo(1)) richiede che Foo essere MoveConstructible. MoveConstructible potrebbe essere soddisfatto da un costruttore di movimento accessibile o anche da un costruttore di copia accessibile. Ma Foo non ha né. Per risolvere il problema è possibile:

class Foo 
{ 
    public: 
     Foo(int i) : i_(i) {} 
     Foo(const Foo&) = default; 
     Foo& operator=(const Foo&) = default; 

     int i_; 
}; 

I.e. imposta i membri di copia predefiniti e rimuove il membro di spostamento eliminato.

In generale non è una buona idea eliminare esplicitamente i membri del movimento. Se si desidera che una classe sia copiabile ma non "mobile", dichiarare esattamente come si farebbe in C++ 03: dichiarare/definire i membri della copia. Puoi lasciare che i membri della copia siano generati dal compilatore con = default e che contino ancora come una dichiarazione utente. E non dichiarare membri del movimento. Sposta i membri che non esistono non sono gli stessi membri del movimento eliminato.

I membri di spostamento eliminati indicano che non è possibile costruire una copia di Foo da un valore di rvalore, anche se il costruttore di copia avrebbe funzionato correttamente. Questo è raramente l'intento desiderato.

Anche se si desidera che la classe non sia copiabile o mobile, è meglio eliminare solo i membri della copia e lasciare i membri spostati non dichiarati (ovvero non esistono). Se riesci a rivedere il codice (incluso il tuo), e vedere i membri delle mosse eliminate, sono quasi sicuramente errati o al meglio superflui e confusi.

Un giorno qualcuno troverà un buon caso d'uso per i membri del movimento cancellati. Ma sarà un caso d'uso raro. Se si vede un tale modello nel codice, si dovrebbe aspettare il codice autore per avere una spiegazione molto buona. In caso contrario, è probabile che i membri di movimento eliminati siano semplicemente errati (nel migliore dei casi superflui). Ma il lato positivo questo errore si mostrerà al momento della compilazione, invece che in fase di esecuzione (come nel tuo esempio).

Ecco un grafico di sintesi di ciò che il compilatore implicitamente fare quando si dichiara in modo esplicito qualsiasi dei membri speciali. Quei quadrati colorati rosso rappresentano un comportamento disapprovato.

enter image description here

= default e = delete contano come dall'utente dichiarato.

Click here se si desidera visualizzare l'intero piano di scorrimento.

+1

Potrebbe essere utile copiare alcuni di questi contenuti su [la tua risposta qui] (http://stackoverflow.com/ a/26492184/1708801) In realtà ho trovato questo in cerca di ulteriori dettagli, +1. –

+0

@ShafikYaghmour: Grazie, questo è un buon consiglio, fatto. –

+0

Se voglio rendere una classe copiabile ma non mobile, è davvero sufficiente lasciare che i membri della copia siano generati implicitamente? Questa [risposta] (http://stackoverflow.com/a/4944131) dice che i membri del movimento verranno generati a meno che non ci sia un utente dichiarato membro di movimento o distruttore. – guini

0

Tu dici:

posso più utilizzarlo in un vettore:

Tuttavia, dato che C++ 11, i requisiti per l'uso in un vettore sono minime; invece, ogni operazione vettoriale ha i propri requisiti. Quindi, in effetti, è possibile utilizzare Foo in un vettore, ma si è limitati a operazioni che non hanno la possibilità di richiedere che l'oggetto venga spostato o copiato.

Per esempio si può scrivere:

std::vector<Foo> w(5); 
w.pop_back(); 

e si può anche chiamare w[0].some_member_function(); e così via.

Tuttavia non si può scrivere qualsiasi dei seguenti, a causa del modo in cui è stata definita Foo:

std::vector<Foo> w = { 1, 2, 3 }; // Requires moving elements out of initializer_list 
w.resize(5);  // Resize may mean reallocation which may require elements to be moved to new location 
w.push_back(5);  // ditto 
w.erase(w.begin()); // Erasing an element may require other elements to move to fill its place 
w[0] = 1;   // operator= is implicitly deleted as described in Howard's answer (but you could explicitly default it to make this work) 

Se si desidera avere Foo essere copiabile, ma non mobile - e hanno anche eventuali scenari di spostamento ripiegare per usare una copia - allora l'unico modo per farlo è di non dichiarare alcun costruttore di mosse; e forzano l'inibizione del costruttore di movimento generato dal compilatore dichiarando un distruttore, un costruttore di copia e/o un operatore di assegnazione della copia come mostrato nella tabella di Howard.

Vedere l'ultimo esempio di codice nella risposta di Howard per un esempio di questo.

per essere chiari ci sono in realtà diversi stati possibili per il costruttore mossa, non tutti mostrato nella tabella di Howard:

  1. Definito come cancellato, e user-definito
  2. Definito come cancellati, e non utenti -defined (questo accade quando un utente non è mobile)
  3. stabilizzati e definiti dall'utente
  4. stabilizzati e non dall'utente
  5. fornita dall'utente (cioè definito dall'utente e né defaul Ted ne cancellato)
  6. non dichiarate

Nei casi 1, 3, 4, 5 di cui sopra, il costruttore mossa è trovato con delibera sovraccarico. Nei casi 2 e 6 non viene trovato dalla risoluzione di sovraccarico; e uno scenario di copia/spostamento come push_back(Foo(1)); ricadrà su un costruttore di copie (se presente).

Problemi correlati