2011-09-04 20 views
18

Ho un sindacato che assomiglia a questo:membro dell'Unione ha un costruttore di copia non banale

union { 
    int intValue; 
    double doubleValue; 
    std::string stringValue; 
    void *pointerValue; 
} values; 

Quando compilo che, ricevo questo messaggio di errore (sì, l'ho fatto #include <string>):

./Value.hh:19:19: error: union member 'stringValue' has a non-trivial copy constructor     
     std::string stringValue;                   
       ^                     
/Developer/SDKs/MacOSX10.7.sdk//usr/include/c++/4.2.1/bits/basic_string.h:434:7: note: because   
     type 'std::basic_string<char>' has a user-declared copy constructor        
     basic_string(const basic_string& __str);               
    ^

posso compilare utilizzando questo comando:

$ clang++ *.cc -isysroot /Developer/SDKs/MacOSX10.7.sdk/ -shared 

Come posso usare un std::string in un sindacato?

+0

Probabilmente non vuoi usare un sindacato. –

risposta

22

Non è possibile.

Un'unione combina due funzionalità: la possibilità di memorizzare un oggetto che può essere di un numero selezionato di tipi e la capacità di convertire in modo efficace (e definito dall'implementazione) tra questi tipi. Puoi inserire un numero intero e guardare la sua rappresentazione come un doppio. E così via.

Poiché un sindacato deve supportare sia di queste funzionalità (e per pochi altri motivi, come essere in grado di costruirne uno), un'unione impedisce di fare determinate cose. Vale a dire, non puoi mettere oggetti "vivi" in essi. Qualsiasi oggetto che sia "vivo" abbastanza da richiedere un costruttore di copie non predefinito (tra molte altre restrizioni) non può essere un membro di un sindacato.

Dopo tutto, un oggetto unione non ha realmente il concetto di quale tipo di dati viene effettivamente memorizzato. Non memorizza un tipo di dati; memorizza tutti di di loro, allo stesso tempo. Spetta a te essere in grado di pescare il tipo giusto. Quindi, come potrebbe ragionevolmente copiare un valore di unione in un altro?

I membri di un unione devono essere di tipo POD (plain-old-data). E mentre C++ 11 allenta quelle regole, gli oggetti devono ancora avere un costruttore di copie predefinito (o comunque banale). E il costruttore di copie di std::string non è banale.

Quello che si desidera è un boost::variant. Questo è un oggetto che può memorizzare un numero di tipi possibili, proprio come un sindacato. A differenza di un sindacato, tuttavia, è sicuro dal punto di vista del tipo. Quindi sa che cosa è effettivamente nell'unione; è quindi in grado di copiare se stesso e si comporta diversamente come un normale oggetto C++.

5

Non è possibile inserire un std::string in un unione. Non è consentito dal linguaggio C++ perché non è sicuro. Considera che la maggior parte delle implementazioni std::string ha un puntatore ad una memoria dinamica che contiene il valore della stringa. Considera anche che non c'è modo di sapere quale membro del sindacato è attualmente attivo.

Un'implementazione non può chiamare il distruttore di std::string, perché non sa che l'oggetto std::string è il membro correntemente attivo, ma se non chiama il distruttore, la memoria sarà trapelata.

+3

Credo in C++ 11 che puoi inserire tipi con costruttori/distruttori non banali in un sindacato, ma devi chiamarli esplicitamente con la sintassi del posizionamento new e t. ~ T(). 9.5.3-9.5.4 –

1

union non possono avere membro seguenti tipi §9.5/1:

Un oggetto di una classe con un costruttore non banale (12.1), un costruttore di copia non banale (12.8), un non- trivial destructor (12.4), o un operatore di assegnazione di copia non banale (13.5.3, 12.8) non può essere un membro di un sindacato, né può un array di tali oggetti.

Quindi, o si definisce un puntatore a std :: string come:

union { 
    int intValue; 
    double doubleValue; 
    std::string *stringValue; //pointer 
    void *pointerValue; 
} values; 

o, unione uso spinta che è conosciuto come Boost.Variant

+0

Ti dispiacerebbe dare un'occhiata al mio commento sulla risposta di Alok (per evitare di ripetermi) e farmi sapere qualche idea? –

3

Come per il C++ standard §9.5.1:

Un oggetto di una classe con un costruttore non banale, un costruttore di copia non banale, un distruttore non banale o un operatore di assegnazione di copia non banale non possono essere un membro di au nion.

Quindi, i membri di un'unione non possono avere costruttori, distruttori, funzioni membro virtuali o classi base. Quindi non puoi usare std :: string come membro del sindacato.

Soluzione alternativa:

È possibile utilizzare boost :: variante o boost :: qualsiasi.

+0

Sembra che devo indagare sul motivo per cui sono in grado di mettere una classe con un comando di copia personalizzato 'operator = (T const & that)' in un 'union', tutto bene ... molte volte. Questa restrizione è stata revocata in C++ 14, o il 'union' 'sta ricadendo' in un altro, implicito assegnamento di copia, _e.g._ operator = (T that)'? –

+0

^Credo che quanto sopra fosse solo il compilatore non essendo abbastanza severo con me. Ora evito questo schema: nessuno dei miei membri 'union' ha cose che non dovrebbero. E funzionano tutti ancora alla grande. :) –

2

Sfortunatamente, non è possibile utilizzare tipi non POD (plain old data) in un unione. Una soluzione abbastanza semplice e tipica è quella di racchiudere l'unione in una struttura, e spostare l'istanza non POD dall'unione alla struct.

Ad esempio:

struct Value { 
    union { 
     int intValue; 
     double doubleValue; 
     void *pointerValue; 
    }; 
    std::string stringValue; 
}; 

Value value; 

// Demonstration of accessing members: 

value.intValue = 0; 
value.doubleValue = 0.0; 
value.pointerValue = NULL; 
value.stringValue = "foo"; 

Dovete comunque pagare un prezzo per questo - l'occupazione di memoria del Value struct sarà più grande di quella del sindacato originale.

Problemi correlati