Cosa significa "default" dopo una dichiarazione di funzione di una classe?


221

Ho visto defaultusato accanto alle dichiarazioni di funzioni in una classe. Che cosa fa?

class C {
  C(const C&) = default;
  C(C&&) = default;
  C& operator=(const C&) & = default;
  C& operator=(C&&) & = default;
  virtual ~C() { }
};

26
Che cosa fa il "&" che precede il "=" nelle dichiarazioni dell'operatore di assegnazione?
Dshin,

6
@dshin Questa è una riqualificazione di una funzione membro .
Kane,

Risposte:


249

È una nuova funzionalità di C ++ 11 .

Significa che si desidera utilizzare la versione generata dal compilatore di quella funzione, quindi non è necessario specificare un corpo.

Puoi anche usare = deleteper specificare che non vuoi che il compilatore generi automaticamente quella funzione.

Con l'introduzione dei costruttori di mosse e degli operatori di assegnazione di mosse, le regole per la generazione di versioni automatiche di costruttori, distruttori e operatori di assegnazione sono diventate piuttosto complesse. Usare = defaulte = deleterendere le cose più facili in quanto non è necessario ricordare le regole: dite semplicemente cosa volete che accada.


17
= deleteè più forte: significa che è vietata l'utilizzo di tale funzione, sebbene partecipi ancora alla risoluzione del sovraccarico.
Deduplicatore il

2
Ma, se vogliamo usare la definizione di generazione del compilatore, non dovremmo saltare la scrittura di quella funzione invece di "scriverla prima e quindi assegnarla come predefinita"?
Mayank Jindal,

47

Questa è una nuova funzionalità C ++ 0x che dice al compilatore di creare la versione predefinita del rispettivo costruttore o operatore di assegnazione, ovvero quella che esegue semplicemente la copia o sposta l'azione per ciascun membro. Ciò è utile perché il costruttore di spostamento non è sempre generato per impostazione predefinita (ad esempio se si dispone di un distruttore personalizzato), a differenza del costruttore di copie (e allo stesso modo per l'assegnazione), ma se non c'è nulla di non banale da scrivere, è meglio lasciare che il compilatore gestirlo piuttosto che spiegarlo ogni volta.

Si noti inoltre che non verrà generato un costruttore predefinito se si fornisce qualsiasi altro costruttore non predefinito. Se vuoi ancora anche il costruttore predefinito, puoi usare questa sintassi per fare in modo che il compilatore ne faccia uno.

Come altro caso d'uso, ci sono diverse situazioni in cui un costruttore di copie non verrebbe generato implicitamente (ad esempio se si fornisce un costruttore di spostamenti personalizzato). Se desideri ancora la versione predefinita, puoi richiederla con questa sintassi.

Vedere la Sezione 12.8 della norma per i dettagli.


5
Anche se non è solo per i costruttori e le assegnazioni, ma vale anche per operator new/new[], operator delete/delete[]e le loro sovraccarichi.
Sebastian Mach,

21

È nuovo in C ++ 11, vedi qui . Può essere abbastanza utile se hai definito un costruttore, ma vuoi usare i valori predefiniti per gli altri. Prima di C ++ 11 dovresti definire tutti i costruttori dopo averne definito uno, anche se equivalgono ai valori predefiniti.

Si noti inoltre che in determinate situazioni è impossibile fornire un costruttore predefinito definito dall'utente che si comporti come quello del compilatore sintetizzato sia con l' inizializzazione predefinita che con il valore . defaultti consente di riavere quel comportamento.


5
per quanto riguarda il secondo paragrafo, puoi fornire un esempio?
John Smith,

11

Un altro caso d'uso che non vedo menzionato in queste risposte è che ti permette facilmente di cambiare la visibilità di un costruttore. Ad esempio, forse vuoi che una classe di amici sia in grado di accedere al costruttore di copie, ma non vuoi che sia disponibile pubblicamente.


1

Bozza standard C ++ 17 N4659

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 11.4.2 "Funzioni esplicitamente predefinite":

1 Una definizione di funzione del modulo:

attribute-specifier-seq opt decl-specifier-seq opt declarator virt-specifier-seq opt = default ;

viene chiamata definizione esplicitamente predefinita. Una funzione che è esplicitamente in default deve

  • (1.1) - essere una funzione membro speciale,

  • (1.2) - hanno lo stesso tipo di funzione dichiarata (tranne che per qualificatori di ref eventualmente diversi e salvo che nel caso di un costruttore di copie o di un operatore di assegnazione di copie, il tipo di parametro può essere "riferimento a T non costante", dove T è il nome della classe della funzione membro) come se fosse stato implicitamente dichiarato, e

  • (1.3) - non ha argomenti predefiniti.

2 Una funzione esplicitamente predefinita che non è definita come eliminata può essere dichiarata constexpr solo se fosse stata dichiarata implicitamente come constexpr. Se una funzione viene esplicitamente impostata sulla prima dichiarazione, viene implicitamente considerata constexpr se la dichiarazione implicita lo fosse.

3 Se una funzione esplicitamente predefinita viene dichiarata con un specificatore noexcept che non produce la stessa specifica di eccezione della dichiarazione implicita (18.4), allora

  • (3.1) - se la funzione è esplicitamente predefinita sulla sua prima dichiarazione, è definita come eliminata;

  • (3.2) - in caso contrario, il programma è mal formato.

4 [Esempio:

struct S {
  constexpr S() = default;            // ill-formed: implicit S() is not constexpr
  S(int a = 0) = default;             // ill-formed: default argument
  void operator=(const S&) = default; // ill-formed: non-matching return type
  ~ S() noexcept(false) = default;    // deleted: exception specification does not match
private:
  int i;                              // OK: private copy constructor
  S(S&);
};
S::S(S&) = default;                   // OK: defines copy constructor

- fine esempio]

5 Le funzioni esplicitamente predefinite e le funzioni dichiarate implicitamente sono chiamate collettivamente funzioni predefinite e l'implementazione fornirà loro definizioni implicite (15.1 15.4, 15.8), il che potrebbe significare definirle come cancellate. Una funzione viene fornita dall'utente se viene dichiarata dall'utente e non viene esplicitamente modificata o eliminata nella sua prima dichiarazione. Una funzione fornita dall'utente in modo esplicito (ovvero, esplicitamente in difetto dopo la sua prima dichiarazione) è definita nel punto in cui è esplicitamente predefinita; se tale funzione è implicitamente definita come eliminata, il programma è mal formato. [Nota: la dichiarazione di una funzione come predefinita dopo la sua prima dichiarazione può fornire un'esecuzione efficiente e una definizione concisa, consentendo al contempo un'interfaccia binaria stabile a una base di codice in evoluzione. - nota finale]

6 [Esempio:

struct trivial {
  trivial() = default;
  trivial(const trivial&) = default;
  trivial(trivial&&) = default;
  trivial& operator=(const trivial&) = default;
  trivial& operator=(trivial&&) = default;
  ~ trivial() = default;
};
struct nontrivial1 {
  nontrivial1();
};
nontrivial1::nontrivial1() = default;       // not first declaration

- fine esempio]

Quindi la domanda è ovviamente quali funzioni possono essere dichiarate implicitamente e quando ciò accade, che ho spiegato a:

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.