2013-06-20 8 views
49

In Stack Overflow posta Checking the object type in C++11, ho il commento:Perché dobbiamo usare virtual ~ A() = default; invece di virtual ~ A() {} in C++ 11?

In C++ 11 ci troveremo vuole fare virtual ~A() = default; In caso contrario, perderai i costruttori implict mossa.

A cosa serve virtual ~A() = default;? Come mai i costruttori di spostamenti impliciti hanno perso con virtual ~A() {}?

+13

+1 perché sei riuscito a causare risposte sbagliate da 10k + utenti. –

+6

Nota che puoi sempre ottenere il costruttore di move con la stessa meccanica: 'A (A &&) = default;' – Xeo

risposta

42

Il commento non è corretto.

Entrambi:

virtual ~A() = default; 

e

virtual ~A() {} 

sono utente dichiarato. E i membri impliciti del movimento sono inibiti se il distruttore viene dichiarato dall'utente.

[dcl.fct.def.default]/p4 discute dall'utente dichiarato e forniti dall'utente membri particolari:

Una funzione membro speciale è fornito dall'utente se è utente -dichiarato e non esplicitamente predefinito o cancellato nella sua prima dichiarazione.

+1

@HowardHinnant: Quindi in entrambi i casi il compilatore non fornirebbe un costruttore di mosse? – legends2k

+2

@ legends2k: è corretto. –

+0

@Pixelchemist: Penso che tu abbia ragione. Sezione 8.4.2 'Le funzioni implicitamente dichiarate implicitamente e le funzioni implicitamente dichiarate sono chiamate collettivamente funzioni predefinite, e l'implementazione deve fornire definizioni implicite per esse. Una funzione membro speciale è fornita dall'utente se è dichiarata dall'utente e non è esplicitamente impostata nella sua prima dichiarazione. Si tratta di n3337 vale a dire C++ 11 standard + 1 – legends2k

5

Questo commento è errato.

Invece di fornire il proprio costruttore di mosse, se si desidera che il compilatore fornisca uno, uno dei requisiti è che si aspetta che il distruttore venga fornito anche da esso, ovvero un distruttore banale. Tuttavia, lo standard attuale è piuttosto rigido quando è possibile fornire un'implementazione implicita — nell'accettare come viene fornito un distruttore dall'utente. Tutto ciò che ha dichiarato da parte dell'utente si ritiene che l'utente sta prendendo la questione nelle proprie mani e, quindi, non solo questo

~A() { … } 

ma anche questo

~A() = default; 

rende il compilatore non fornisce un distruttore implicito. La prima è una definizione e quindi anche una dichiarazione; la seconda è solo una dichiarazione. In entrambi i casi il distruttore è dichiarato dall'utente e quindi impedisce al compilatore di fornire un costruttore di mosse implicito.

Credo che la logica alla base del requisito è che durante mossa risorse di un oggetto vengono spostati in un altro oggetto lasciando l'oggetto originale in uno stato in cui non ha risorse in dinamico di stoccaggio; ma se la tua classe non ha tali risorse allora può essere banalmente spostata, distrutta, ecc. Quando dichiari un distruttore non banale è una stecca per il compilatore che le risorse che gestisci nella classe non sono qualcosa di banale e che per lo più devi fornire non banale spostare, quindi il compilatore non ne fornisce uno.

+4

+1 per commentare il ragionamento dietro questo comportamento. –

+1

Tranne che non è il ragionamento alla base di questo comportamento, perché questo è _non_ il comportamento. –

+0

Risolto ora per risolvere la domanda degli OP. – legends2k

27

In questo post https://stackoverflow.com/a/17204598/260127, ho il commento:

In C++ 11 ci troveremo vuole fare virtual ~A() = default; In caso contrario, perderai i costruttori implict mossa.

Il commento è errato.

Anche default Ed, che distruttore è "user-dichiarato" (anche se nota che non è anche "fornito dall'utente").

#include <iostream> 

struct Helper 
{ 
    Helper() {} 
    Helper(const Helper& src) { std::cout << "copy\n"; } 
    Helper(Helper&& src)  { std::cout << "move\n"; } 
}; 

struct A 
{ 
    virtual ~A() {} 
    Helper h; 
}; 

struct B 
{ 
    virtual ~B() = default; 
    Helper h; 
}; 

struct C 
{ 
    Helper h; 
}; 


int main() 
{ 
    { 
     A x; 
     A y(std::move(x)); // outputs "copy", because no move possible 
    } 

    { 
     B x; 
     B y(std::move(x)); // outputs "copy", because still no move possible 
    } 

    { 
     C x; 
     C y(std::move(x)); // outputs "move", because no user-declared dtor 
    } 
} 

Live demo:

+ g ++ - 4.8 -std = C++ 11 -O2 -Wall main.cpp -pthread
+ ./a.out
copia
copia
spostare

Quindi non si è "perso" qualcosa di — non c'era mo ve funzionalità lì per cominciare!

Ecco il passo standard che vieta una mossa costruttore implicita entrambi casi:

[C++11: 12.8/9]: Se la definizione di una classe X non dichiara esplicitamente un costruttore mossa, uno sarà implicitamente dichiarato come default se e solo se

  • X non ha un costruttore di copia user-dichiarato,
  • X non ha un operatore di assegnamento copia utente-dichiarato,
  • X non ha un operatore di assegnamento mossa dall'utente dichiarato,
  • X non hai distruttore dall'utente dichiarato, e
  • il costruttore mossa avrebbe non essere definito implicitamente come cancellato.

Bootnote

Non sarebbe male se una futura versione dello standard effettivamente elencato i significati precisi di termini come "user-dichiarato". Non v'è, almeno, questo:

[C++11: 8.4.2/4]:[..] una funzione membro speciale è fornito dall'utente se è user-dichiarato e non esplicitamente default o cancellato dalla sua prima dichiarazione. [..]

Si può assumere la distinzione qui per implicazione.

+1

Anche se mi chiedo perché lo standard non permetta all'implementazione di fornire un costruttore di mosse quando nessuno è _provided_ dall'utilizzatore, lasciandolo non dichiarato e facendo '= default;' sembra lo stesso. – legends2k

+1

@ legends2k: sono d'accordo; idealmente '12.8/9' potrebbe invece dire" fornito dall'utente ". Probabilmente '12.8/20', anche tu? –

+0

@ legends2k: E, sì, tutti amano un buon banco di prova. : D –