2015-03-26 13 views
16

Le regole per la generazione automatica di funzioni speciali di spostamento (operatore costruttore e assegnazione) in C++ 11 specificano che nessun distruttore può essere dichiarato. La logica è presumibilmente che, se devi fare qualcosa di speciale nella distruzione, che una mossa potrebbe non essere sicura.C++ 11 distruttori virtuali e generazione automatica di funzioni speciali di spostamento

Tuttavia, per le chiamate di distruttore corrette nel polimorfismo, è necessario dichiarare un distruttore di classi di base come virtuale (altrimenti l'eliminazione di un'istanza di una sottoclasse attraverso un puntatore della sua classe base non concatena correttamente il distruttore).

Suppongo, quindi, che anche un distruttore vuoto impedisca al compilatore di generare automaticamente funzioni di spostamento speciali. Come in:

class Base { 
    virtual ~Base() { } 
}; 

È possibile, tuttavia, di default il distruttore, come in:

class Base { 
    virtual ~Base() = default; 
} 

Così Domanda 1: Sarà questo permettere al compilatore di generare automaticamente le funzioni speciali mossa?

C'è un problema con il distruttore di default esplicito, tuttavia. Almeno nel caso di GCC 4.8.2, la firma è implicitamente cambiata in non eccezionale. Come in:

class Base { 
    virtual ~Base() = default; // compiler changes to: 
    // virtual ~Base() noexcept; 
} 

Mentre non ho alcun problema con noexcept in un distruttore, questo avrebbe rotto il seguente codice "client":

class Sub : public Base { 
    virtual ~Sub(); // this declaration is now "looser" because of no noexcept 
} 

domanda SO 2 è più al punto: esiste un modo per consentire la generazione automatica di funzioni speciali di spostamento in C++ 11 e consentire il corretto concatenamento del distruttore alle sottoclassi (come descritto sopra), tutto senza interrompere la sottoclasse (codice "client")?

+1

Perché '~ Sub' è più lento? I distruttori senza specifiche di eccezione sono impostati su "noexcept". – Pradhan

+0

La domanda sembra ben definita, ma per interesse, hai un esempio del perché questo potrebbe essere necessario? Questo sembra uno strano miscuglio di valori e semantica di riferimento. Mi sembra che nei casi in cui si voglia utilizzare il movimento (o anche la copia) non si voglia un comportamento polimorfico. E più al punto che la copia o lo spostamento predefinito sicuramente non avrà alcun comportamento polimorfico. – tahsmith

+0

@tahsmith l'obiettivo è di rielaborare le classi base in modo tale che esse e le loro sottoclassi possano trarre vantaggio dalla semantica del movimento senza infrangere le sottoclassi. – notlesh

risposta

16
  1. No, un distruttore predefinito è ancora considerato definito dall'utente, quindi impedirà la generazione di operazioni di spostamento. Dichiarare anche le operazioni di spostamento default -ed per generare il compilatore.

  2. È necessario dichiarare le operazioni di spostamento solo come default -ed nella classe base. Nella classe derivata, il distruttore non sarà più definito dall'utente (a meno che non lo dici esplicitamente), quindi le operazioni di spostamento non verranno eliminate.

Quindi quello che farei è il seguente:

class Base 
{ 
    virtual ~Base() = default; 
    Base(Base&&) = default; 
    Base& operator=(Base&&) = default; 
    // probably need to think about copy operations also, as the move disables them 
    Base(const Base&) = default; 
    Base& operator=(const Base&) = default; 
}; 

consiglio vivamente questo discorso da parte della persona che ha contribuito probabilmente il più alla semantica move: http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014

O, se puoi mettere le mani su, leggere Articolo 17: Capire la generazione di funzioni membro speciale dall'ottimo libro di Scott Meyers Effective Modern C++. Questo problema è spiegato in modo eccellente.

PS: Penso che dovresti pensare un po 'di più alle tue classi base. La maggior parte delle volte, dovresti usare classi astratte, quindi non sarà necessario copiare/spostare istanze di esse.

PSS: Penso che da distruttori di default sono contrassegnati noexcept in C++ 11/14, in modo da non esplicitamente specificando che non dovrebbe causare problemi:

costruttori Ereditare e costruttori implicitamente dichiarata predefinito , copia i costruttori, sposta i costruttori, i distruttori, gli operatori di assegnazione copia, gli operatori di assegnazione movimento sono tutti noexcept (true) per impostazione predefinita, a meno che non sia richiesto di chiamare una funzione che non è nient'altro (false), nel qual caso queste funzioni sono noxcept (false).

+0

Grazie, penso che questo risolva il problema (e risponda alle domande). Stavo davvero leggendo l'articolo 17 nel libro di Scott quando mi è venuta in mente questa domanda :) – notlesh

+0

@stephelton lieto che abbia aiutato. Riguardo la specifica "looser nothrow", penso di essermi imbattuto in questo prima (g ++ 4.8.x), è un problema del compilatore, proverò a trovare il link e lo postò qui – vsoftco

+2

@stephelton lo ha trovato, vedi qui: http : //stackoverflow.com/questions/11497252/default-destructor-nothrow – vsoftco

Problemi correlati