perché eliminare esplicitamente il costruttore?


93

Quando / perché dovrei voler eliminare esplicitamente il mio costruttore? Supponendo che il motivo sia per impedirne l'utilizzo, perché non farlo e basta private?

class Foo
{ 
  public: 
    Foo() = delete; 
};

14
In un = defaultcerto senso va bene con , nemmeno la classe può usarlo, e personalmente preferisco vedere Uso della funzione cancellata. over La funzione è privata. Il primo afferma esplicitamente "Questo non è pensato per essere utilizzato". Se ne esce qualcosa, il fatto che la classe non sia in grado di usarlo fa effettivamente una differenza semantica.
chris

15
Onestamente penso che la gente stia iniziando a diventare aggressiva con i voti vicini. Non vedo come questo non sia costruttivo.
Luchian Grigore

4
@LuchianGrigore: d'accordo. Mi chiedevo perché io stesso la comunità sia diventata molto più rigida. Non vedo il punto.
Ed S.

11
Dato che uso raramente C ++ 11, questo è più informativo per me di quanto l'OP probabilmente non si renda nemmeno conto. Non sapevo nemmeno che potessi taggare un costruttore per delete. Sia la domanda che la risposta di Luchian si qualificano facilmente come costruttive. Chiunque non respiri i punti più fini di C ++ 11 ma dovrà presto ottenere qualcosa da entrambi.
WhozCraig

Risposte:


87

Che ne dite di:

//deleted constructor
class Foo
{ 
  public: 
    Foo() = delete;     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //illegal
}

contro

//private constructor
class Foo
{ 
  private: 
    Foo() {}     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //legal
}

Sono fondamentalmente cose diverse. privateti dice che solo i membri della classe possono chiamare quel metodo o accedere a quella variabile (o agli amici ovviamente). In questo caso, è legale per un staticmetodo di quella classe (o qualsiasi altro membro) chiamare un privatecostruttore di una classe. Questo non vale per i costruttori eliminati.

Campione qui .


3
Non è necessario dichiarare affatto Foo (), se si dichiara Foo (int). Foo () non verrà generato e quindi Foo f non è comunque valido. Quindi il tuo esempio non mostra il caso per il costruttore cancellato. Guarda tu stesso - ideone.com/mogiIF
segna il

1
@mark Ho scritto 2 costruttori per dimostrare il punto. Modificherò in modo che sia chiaro a tutti.
Luchian Grigore

1
Capisco la differenza, semplicemente non capisco il valore aggiunto dell'istruzione delete in generale e per un costruttore in particolare. Dopo tutto, potrei specificare un costruttore predefinito privato senza il corpo. Quindi anche il codice fallisce, solo durante il collegamento. Bene, posso vedere che l'eliminazione trasmette l'intenzione in modo più esplicito, ma questo è tutto.
segna il

11
@mark Sì, quello sarebbe il modo C ++ 98 di fare le cose. Ma IMHO, trasmettere chiaramente l'intenzione è in realtà una cosa molto importante nella programmazione in generale. In questo caso, alcuni lettori potrebbero vedere un costruttore privato indefinito e presumere che sia accidentale e aggiungere semplicemente una definizione, specialmente se la definizione è banale come un costruttore predefinito (Sì, avere un commento aiuta, ma preferiremmo di gran lunga il compilatore -applicazione sull'applicazione dei commenti). Avendo il nostro intento più chiaro, otteniamo anche un messaggio di errore molto migliore che dice "esplicitamente cancellato" piuttosto che "riferimento non definito".
mpark

2
Onestamente non capisco come questo risponda alla domanda principale. La domanda nel titolo e la prima domanda di OP nel post era: quando / perché dovrei voler eliminare esplicitamente il mio costruttore?
Alexander Bolinsky

11

perché eliminare esplicitamente il costruttore?

Un altro motivo:
io usodelete quando voglio assicurarmi che una classe venga chiamata con un inizializzatore. Lo considero un modo molto elegante per ottenere questo risultato senza controlli di runtime.

Il compilatore C ++ esegue questo controllo per te.

class Foo
{
   public:
       Foo() = delete;
       Foo(int bar) : m_bar(bar) {};
   private:
       int m_bar;
}

Questo codice, molto semplificato , assicura che non ci sia istanziazione come questa:Foo foo;


12
La dichiarazione cancellata non è necessaria qui. Viene automaticamente eliminato con qualsiasi costruttore fornito dall'utente
Mike Lui

5
Per chiarire il commento di @MikeLui, la dichiarazione cancellata non è necessaria al compilatore . Ci sono numerosi casi in cui un codice come questo dovrebbe essere incluso per dichiarare l'intento ad altri programmatori .
Jeff G

Insieme a dichiarare il tuo intento, crea un posto ovvio per documentare il motivo della sua cancellazione nell'interfaccia pubblica, e inoltre l'errore del compilatore sarà qualcosa di breve come "uso della funzione cancellata". Se Fooavesse numerosi costruttori, ma non uno predefinito, Foo foo;causerebbe un errore molto più lungo elencando tutti i costruttori implicitamente definiti, protetti e privati ​​che non è riuscito a trovare.
sigma

Continuo a non capire come la riga aggiuntiva con la dichiarazione del costruttore che la parola chiave "= cancella" dichiari l'idea di "nessun costruttore predefinito" meglio di ... semplicemente nessun costruttore predefinito? Esempio: non voglio dichiarare la variabile "a" nel mio codice - cosa è meglio, scrivere "// int a; // non c'è bisogno di definire la variabile a" o semplicemente non scrivere nulla su questa variabile nel codice?
Ezh

2

Ho incontrato i ctors predefiniti dichiarati come "cancellati" nel codice sorgente di LLVM (in AlignOf.h per esempio). I modelli di classe associati si trovano solitamente in uno speciale spazio dei nomi chiamato "llvm :: detail". L'intero scopo lì credo fosse che consideravano quella classe solo come una classe di aiuto. Non hanno mai inteso istanziarli; solo per usarli nel contesto di altri modelli di classe con alcuni trucchi di metaprogrammazione che vengono eseguiti in fase di compilazione.

Per esempio. c'è questo modello di classe AlignmentCalcImpl che viene utilizzato solo all'interno di un altro modello di classe chiamato AlignOf come parametro per l'operatore sizeof (.). Quell'espressione può essere valutata in fase di compilazione; e non c'è bisogno di istanziare il modello -> quindi perché non dichiarare il ctor predefinito delete per esprimere questa intenzione.

Ma è solo una mia ipotesi.

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.