2014-11-01 14 views
12

La domanda esistente su Why can't I initialise an array of objects if they have private copy constructors? si riferisce specificamente a C++ 03. So da quella domanda che quello che sto cercando di fare non è permesso in C++ 03 ma ho pensato che dovrebbe essere possibile in C++ 11Come inizializzare l'array di classi con il costruttore di copie cancellato (C++ 11)

Ho una classe non mobile (chiamala Child) e Ho bisogno di inizializzare un array di Child nel costruttore di un'altra classe (chiamalo Parent). Con "non-mobile" intendo che l'indirizzo di un oggetto Child deve rimanere uguale durante la sua vita. Qual è il modo corretto per farlo?

Con C++ 11 Ho provato quanto segue:

class Child 
{ 
public: 
    Child (int x) {} 
    ~Child() {} 

    Child (const Child &) = delete; 
}; 

class Parent 
{ 
public: 
    Parent() : children {{5}, {7}} {} 

private: 
    Child children[2]; 
}; 

Questo codice compila bene con Clang 3.5.0, ma GCC 4.9.1 lamenta che sto cercando di utilizzare il costruttore di copia eliminata:

test.cc: In constructor ‘Parent::Parent()’: 
test.cc:13:35: error: use of deleted function ‘Child::Child(const Child&)’ 
    Parent() : children {{5}, {7}} {} 
           ^
test.cc:7:5: note: declared here 
    Child (const Child &) = delete; 
    ^

ho letto circa la differenza tra la copia-inizializzazione e-inizializzazione diretta (here e here, per esempio), e voglio evitare di chiamare il costruttore di copia utilizzando-inizializzazione diretta. Sto ottenendo la sintassi sbagliata? È un bug in GCC? O è quello che sto cercando di fare solo non è possibile?

+1

Mi sembra un bug clang e non un bug di gcc. clang non riesce a compilare il codice se lo si cambia in 'children {Child {5}, Child {7}}', che dovrebbe comportarsi in modo identico a ciò che hai postato. Una soluzione alternativa sarebbe usare un 'vector' e inserire gli oggetti' Child'. – Praetorian

+0

g ++ ha esito positivo con 'Child child [2] {{5}, {7}};' che dovrebbe essere identico alla versione in cui si verifica lo stesso inizializzatore nell'elenco di inizializzatori ctor; entrambi sono coperti da [dcl.init.list]/3 –

+1

Leggendo le sezioni sull'inizializzazione, questo codice sembra corretto; 'children [2] = {{5}, {7}}' dice che 'children [0]' è copy-initializaed da '{5}', cioè è uguale a 'Child c = {5};', e questo è coperto di nuovo da [dcl.init.list] che richiama il costruttore per 'c' che prende' int' (senza coinvolgere una copia). –

risposta

3

Sono d'accordo con i commenti che questo sembra essere un bug GCC (segnalato come 63707).

Non riesce a compilare solo quando il tipo nella matrice ha un distruttore definito dall'utente, il che non ha senso per me.

+0

A volte fallisce anche senza distruttore: http://stackoverflow.com/questions/31906483/class-member-array-of-objects-without-default-constructor-and-deleted-copy-cont – peku33

+0

@ peku33, sì, I diede un esempio simile nel rapporto sui bug di GCC. Il problema si verifica con i distruttori non banali, non solo quelli definiti dall'utente. –

-1

mi è venuto attraverso un problema simile, vale a dire che questo codice

#include <iostream> 

class Widget { 
public: 
    Widget(int i) { std::cout << "Ctor " << i << std::endl; } 

    Widget(const Widget&); // = delete; 
}; 

int main() { 
    Widget w = 123; 
} 

compilato e ha dato il risultato atteso, ma dopo commentando la = delete non è riuscito a compilare con gcc-4.9.

Dopo aver letto lo standard I credo la risposta si trova nel secondo elemento di massima indentazione in 8.5/16, che viene citato di seguito.

Quello che sembra fondamentalmente per accadere è che il compilatore vuole concettualmente creare una temporanea di tipo Widget e diretta inizializzare l'oggetto reale w da quella temporanea tramite il costruttore di copia. Poiché il costruttore di copie viene cancellato, la compilazione si interrompe. Se il costruttore della copia non è stato cancellato, il compilatore si renderebbe conto in seguito che potrebbe elidere la copia, ma non arriva così lontano.

Ecco la parte rilevante:

[...] per la [...] casi copy-inizializzazione [...] sequenze di conversione definiti dall'utente che possono convertire dal tipo di sorgente il tipo di destinazione [...] viene enumerato come descritto in 13.3.1.4 e il migliore viene scelto tramite la risoluzione di sovraccarico (13.3). [...] La funzione selezionata viene chiamata con l'espressione initializer come argomento; se la funzione è un costruttore, la chiamata inizializza un temporaneo della versione cv-non qualificata del tipo di destinazione. [...] Il risultato della chiamata (che è il temporaneo per il caso del costruttore) viene quindi utilizzato per l'inizializzazione diretta, in base alle regole precedenti, dell'oggetto che è la destinazione dell'inizializzazione della copia. In alcuni casi, è consentita un'implementazione per eliminare la copia inerente a questa inizializzazione diretta costruendo il risultato intermedio direttamente nell'oggetto da inizializzare; vedi 12.2, 12.8.

Ma potrei sbagliarmi visto che c'è molto nelle parti [...] che non ho capito.

+3

Non penso che questo sia veramente correlato. 'Widget w = 123;' usa il costruttore di copie, 'Widget w {123};' è l'inizializzazione diretta. –

+1

Questa è sicuramente una situazione diversa. Creare un 'Widget' nello stack richiede un distruttore accessibile, perché viene chiamato automaticamente per gli oggetti stack. Se hai un distruttore cancellato non puoi crearlo sullo stack. Questo non ha nulla a che fare con la domanda. –

+0

@JonathanWakely Nel mio esempio non è il distruttore che viene eliminato. –

Problemi correlati