2015-07-29 13 views
5

Sto provando ad usare un sindacato (C++) che ha alcune variabili non primitive, ma sono bloccato cercando di creare il distruttore per quella classe. Come ho letto, non è possibile indovinare quale variabile del sindacato viene utilizzata, quindi non esiste un distruttore implicito e, poiché sto usando questa unione sullo stack, il compilatore commette errori che il distruttore viene cancellato. L'unione è la seguente:Come scrivere distruttore per classe union-like

struct LuaVariant { 
    LuaVariant() : type(VARIANT_NONE) { } 

    LuaVariantType_t type; 
    union { 
     std::string text; 
     Position pos; 
     uint32_t number; 
    }; 
}; 

La variabile type stive che campo dell'unione viene utilizzato (a scelta tra un enum), ai fini della lettura da parte dell'Unione e potrebbe essere usato per indovinare il valore quello che dovrebbe essere cancellato Ho provato alcuni approcci diversi ma nessuno di loro ha funzionato. Prima di tutto, ho appena provato il distruttore predefinito:

~LuaVariant() = default; 

Non ha funzionato, poiché l'impostazione predefinita è ... cancellata. Così, ho provato scambiando il valore con uno vuoto, in modo che i contenuti sarebbero stati cancellati e non ci sarebbe nessun problema "perde" un valore vuoto:

~LuaVariant() { 
    switch (type) { 
     case VARIANT_POSITION: 
     case VARIANT_TARGETPOSITION: { 
      Position p; 
      std::swap(p, pos); 
      break; 
     } 
     case VARIANT_STRING: { 
      std::string s; 
      std::swap(s, text); 
      break; 
     } 
     default: 
      number = 0; 
      break; 
    } 
}; 

Ma come io non sono un maestro dei sindacati, Non so se questo può causare altri problemi, come la memoria allocata che non viene mai deallocata, o qualcosa del genere. Questa strategia di swap può essere utilizzata senza difetti e problemi?

+0

Invece di vostro trucco di swap, è necessario chiamare in modo esplicito distruttore. Il tuo codice non invoca i distruttori per l'oggetto nell'unione. Questo è probabilmente solo una perdita di risorse piuttosto che UB, ma la soluzione più pulita e corretta è chiamare i distruttori. –

+0

* classe union-like * è il termine standard per un 'union', o una classe/struct che contiene un'unione anonima. –

risposta

2

Se si desidera utilizzare std::string in un unione in C++ 11, si deve chiamare esplicitamente il suo distruttore e il nuovo posizionamento per costruirlo. Esempio da cppreference.com:

#include <iostream> 
#include <string> 
#include <vector> 
union S { 
    std::string str; 
    std::vector<int> vec; 
    ~S() {} // needs to know which member is active, only possible in union-like class 
}; // the whole union occupies max(sizeof(string), sizeof(vector<int>)) 

int main() 
{ 
    S s = {"Hello, world"}; 
    // at this point, reading from s.vec is UB 
    std::cout << "s.str = " << s.str << '\n'; 
    s.str.~basic_string<char>(); 
    new (&s.vec) std::vector<int>; 
    // now, s.vec is the active member of the union 
    s.vec.push_back(10); 
    std::cout << s.vec.size() << '\n'; 
    s.vec.~vector<int>(); 
} 
+0

Buono, ma questa è una grande seccatura. A volte non so quale dei campi viene utilizzato (spingo e sposto lo stack Lua), quindi avrei comunque bisogno di un interruttore per distruggerli, giusto? Inoltre, c'è qualche contro nel mio approccio? – ranisalt

+2

@ranisalt Devi sapere quale dei campi stai usando, per sapere quale è possibile accedere. L'accesso a qualsiasi altro campo rispetto a quello attivo è UB. – emlai

6

Questo raggruppamento (unione + enum valore per il tipo di discriminazione) è chiamato un'unione discriminata.

Spetterà a voi chiamare qualsiasi costruzione/distruzione, perché l'unione stessa non può (se potesse, sarebbe anche in grado di discriminare per tipi inizializzati/non inizializzati all'interno dell'unione, e non sarebbe necessario l'enum).

Codice:

class LuaVariant // no public access to the raw union 
{ 
public: 
    LuaVariant() : type(VARIANT_NONE) { } 
    ~LuaVariant() { destroy_value(); } 

    void text(std::string value) // here's a setter example 
    { 
     using std::string; 
     destroy_value(); 
     type = VARIANT_TEXT; 
     new (&value.text) string{ std::move(value) }; 
    } 
private: 

    void destroy_value() 
    { 
     using std::string; 
     switch(type) 
     { 
     case VARIANT_TEXT: 
      (&value.text)->string::~string(); 
      break; 
     case VARIANT_POSITION: 
      (&value.pos)->Position::~Position(); 
      break; 
     case VARIANT_NUMBER: 
      value.number = 0; 
      break; 
     default: 
      break; 
     } 
    } 

    LuaVariantType_t type; 
    union { 
     std::string text; 
     Position pos; 
     uint32_t number; 
    } value; 
}; 
+1

Avevo bisogno di aggiungere un distruttore vuoto nel mio sindacato per farlo funzionare. Mi sto perdendo qualcosa? Ho avuto un "valore :: ~ valore() è una funzione cancellata" in caso contrario – Julien

Problemi correlati