2013-08-17 8 views
54

È questoÈ un costruttore di spostamento `= default` equivalente a un costruttore di movimenti membro-saggio?

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB}    { } 
    Example(const Example& mE) : a{mE.a}, b{mE.b}  { } 
    Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { } 
    Example& operator=(const Example& mE) { a = mE.a; b = mE.b; return *this; } 
    Example& operator=(Example&& mE)  { a = move(mE.a); b = move(mE.b); return *this; } 
} 

equivalente a questa

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB} { } 
    Example(const Example& mE)   = default; 
    Example(Example&& mE)     = default; 
    Example& operator=(const Example& mE) = default; 
    Example& operator=(Example&& mE)  = default; 
} 

?

+0

Questa potrebbe essere una duplicazione di http://stackoverflow.com/questions/4819936/why-no-default-move-assignment-move-constructor –

+3

@ DieterLücking: chiaramente non è, anche se è su un argomento simile e alcuni le risposte possono coprire un terreno simile. Tuttavia, non chiudiamo ogni singola domanda sulla semantica del movimento come duplicati l'uno dell'altro. –

+0

Nota, ho aggiunto la mia risposta a questa domanda perché al momento cercavo una citazione dallo standard che dimostrasse che erano equivalenti e la risposta accettata non lo fa. Quindi, ho appena trovato la citazione e ho aggiunto la mia risposta. –

risposta

27

Sì, entrambi sono uguali.

Ma

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB} { } 
    Example(const Example& mE)   = default; 
    Example(Example&& mE)     = default; 
    Example& operator=(const Example& mE) = default; 
    Example& operator=(Example&& mE)  = default; 
} 

Questa versione vi permette di saltare la definizione del corpo.

Tuttavia, è necessario seguire alcune regole quando si dichiara explicitly-defaulted-functions:

8.4.2 funzioni in modo esplicito-default [dcl.fct.def.default]

Una definizione di funzione del forma:

attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ; 

è chiamato un esplicitamente-default definizione. Una funzione che viene esplicitamente come insolvente è

  • essere una funzione membro speciale,

  • hanno lo stesso tipo di funzione dichiarata (tranne possibilmente differenti ref-qualificatori e tranne che nel caso di una copia costruttore o copiare operatore di assegnamento, il tipo di parametro può essere “riferimento al non-const T”, dove T è il nome della classe del funzione membro), come se fosse stato implicitamente dichiarata,

  • non avere argomenti predefiniti.

+0

Da quale documento stai citando 8.4.2? Né lo standard C++ 11 né N3690 contengono il testo ", e non hanno una * specifica-eccezione *" in 8.4.2/1. Entrambi dicono in 8.4.2/2: "Una funzione di default esplicito può essere dichiarata' constexpr' solo se sarebbe stata implicitamente dichiarata come 'constexpr', e potrebbe avere una * specifica-eccezione * esplicita solo se è compatibile (15.4) con * specifica-eccezione * sulla dichiarazione implicita. " – Casey

+1

@Casey Buona cattura! Stavo citando l'N3242 ... Ho mescolato i miei documenti ... Ho aggiornato il mio post per citare l'N3690! Grazie per aver segnalato questo! –

+1

Se imposto un costruttore di movimenti e un operatore di assegnazione a '= default', sarò in grado di scambiare con l'oggetto? Non ho bisogno di dichiarare il costruttore come 'noxcept'? Ho provato a mettere sia 'noexcept' che' = default' per entrambi, ma questo non sarebbe stato compilato. – VF1

0

a parte casi molto patologici ... SI.

Per essere più precisi, è necessario considerare anche le eventuali basi Example con le stesse regole. Prima le basi - in ordine di dichiarazione - quindi i membri, sempre in ordine di dichiarazione.

14

Sì, un costruttore mossa stabilizzato eseguirà una mossa membro-saggio della sua base e membri, così:

Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { } 

è equivalente a:

Example(Example&& mE)     = default; 

possiamo vedere questo andando alla sezione draft C++11 standard12.8Copia e spostamento di oggetti di classe paragra ph che dice (sottolineatura mia andare avanti):

un costruttore di copia/spostamento che è in default e non definito come cancellati viene implicitamente definito se è odrused (3.2) o quando è esplicitamente predefinito dopo la prima dichiarazione. [Nota: il costruttore di copia/spostamento è implicitamente definito anche se l'implementazione ha eluso il suo uso di odr (3.2, 12.2). Nota -end] [...]

e il paragrafo 15, che dice:

Il copia implicitamente definito/mossa costruttore per un non-union classe X esegue un copia/spostamento membro delle sue basi e membri. [Nota: inizializzatori brace-or-equal di membri di dati non statici vengono ignorati. Vedere anche l'esempio in 12.6.2. -end note] L'ordine di inizializzazione è lo stesso dell'ordine di inizializzazione delle basi e dei membri in un costruttore definito dall'utente (vedere 12.6.2). Sia x sia il parametro del costruttore o, per il costruttore di movimento, un valore xche si riferisce al parametro. Ogni base o dati non statico membro viene copiato/spostato in modo adeguato al tipo:

  • se l'elemento è un array, ogni elemento è diretto inizializzato con il corrispondente subobject di x;
  • se un membro m ha il tipo di riferimento rvalore T & &, viene inizializzato direttamente con static_cast (x.m);
  • in caso contrario, la base o il membro viene inizializzato direttamente con la corrispondente base o membro di x.

sottoggetti classe base virtuale va inizializzato solo una volta dal implicitamente definito costruttore di copia/spostamento (vedi 12.6.2).

0

È questo equivalente a questo

Sì. Ma è valido solo per le classi che non hanno membri non mobili.Guarda questo esempio:

#include <iostream> 

struct nonmovable 
{ 
    nonmovable() = default; 

    nonmovable(const nonmovable &) = default; 
    nonmovable(  nonmovable &&) = delete; 
}; 

struct movable 
{ 
    movable() = default; 

    movable(const movable &) { std::cerr << "copy" << std::endl; } 
    movable(  movable &&) { std::cerr << "move" << std::endl; } 
}; 

struct has_nonmovable 
{ 
    movable a; 
    nonmovable b; 

    has_nonmovable() = default; 

    has_nonmovable(const has_nonmovable &) = default; 
    has_nonmovable(  has_nonmovable &&) = default; 
}; 

int main() 
{ 
    has_nonmovable c; 
    has_nonmovable d(std::move(c)); // prints copy 
} 

Esso stampa:

copy 

http://coliru.stacked-crooked.com/a/62c0a0aaec15b0eb

Quindi, se una classe ha anche un solo membro non mobile, il costruttore mossa esplicitamente inadempiente utilizzerà i costruttori di copia per tutti i membri, anche per quelli che hanno il costruttore di mosse.

Tuttavia, non è necessario che i membri abbiano costruttori di movimento definiti in modo esplicito o predefinito in modo esplicito. Se il costruttore di movimento non è dichiarato, il costruttore di movimento verrà utilizzato per i membri che hanno il costruttore di movimento e il costruttore di copia verrà utilizzato per i membri che non definiscono il costruttore di spostamenti. Vedere l'esempio:

#include <iostream> 

struct nonmovable 
{ 
    nonmovable() = default; 

    nonmovable(const nonmovable &) { std::cerr << "nonmovable::copy" << std::endl; } 
    //nonmovable(  nonmovable &&) = delete; 
}; 

struct movable 
{ 
    movable() = default; 

    movable(const movable &) { std::cerr << "movable::copy" << std::endl; } 
    movable(  movable &&) { std::cerr << "movable::move" << std::endl; } 
}; 

struct has_nonmovable 
{ 
    movable a; 
    nonmovable b; 

    has_nonmovable() = default; 

    has_nonmovable(const has_nonmovable &) = default; 
    has_nonmovable(  has_nonmovable &&) = default; 
}; 

int main() 
{ 
    has_nonmovable c; 
    has_nonmovable d(std::move(c)); 
} 

Esso stampa:

movable::move 
nonmovable::copy 

http://coliru.stacked-crooked.com/a/fda51f29a4b92881

Ci sono classi che sono mobili ma non copiabile (ad esempio unique_ptr). Esistono classi che possono essere copiate e utilizzare il costruttore di copia per lo spostamento. Ma non conosco alcun motivo per creare una classe che è possibile copiare ma che ha esplicitamente eliminato il costruttore di mosse.