2016-12-21 5 views
1

Während dieser Code ohne Fehler kompiliert wird, bezweifle ich, dass es wie erwartet funktioniert. Ist solches Nisten erlaubt? Ich kann Boost und C++ 17 nicht verwenden.C++: Union, die Klasse enthält es gehört

class Node; 

typedef struct Value {  
    ValueTag type; 

    union {   
     std::int32_t       integerValue; 
     std::float_t       floatValue; 
     bool         boolValue; 
     std::vector<Node>      arrayValue; 
     std::unordered_map<std::string, Node> dictionaryValue; 
    };   
} Value; 

class Node {     
private:   
    Value m_value;   
public:     
    virtual ~Node();  
}; 
+3

Gewerkschaften behandeln Konstruktoren und Destruktoren nicht. Daher ist die Verwendung einer Union mit einem Nicht-POD unsicher (std :: vector ist unsicher). siehe std :: variant und boost :: variant –

+3

Nicht verwandt mit Ihrer Frage, aber warum verwenden Sie 'typedef struct ...'? Strukturen sind wie Klassen und der Strukturname ist ein Typ wie ein Klassenname. –

+1

Weil ich aus einer C-Welt kam. – Ariel

Antwort

-2

bearbeiten Meine ursprüngliche Antwort war falsch/irreführend, plz ignorieren. Aus dem Grund siehe die Kommentare unten. sweet std :: variant oder boost :: variant sowieso :)

Non PlainOldData (POD) sind in c-unicons unsicher.

Speziell für diese proplem std :: Variante kam in den C++ 17 Standard. Sie könnten boost :: variant für frühere Versionen von C++ verwenden.

Das Problem ist, dass die Union nichts über Konstruktoren und Destruktoren weiß. Daher ist es nicht möglich, allgemeinere Dinge mit Elementen einer Vereinigung zu tun, die notwendig wären (z. B. dynamische Speicherzuweisung).

+0

Hilf mir mit dem Downvote? ist es falsch, was ich gesagt habe? –

+0

Sie können sehr gut Verknüpfungen erstellen und verwenden, die nicht-triviale Typen enthalten. Die Umleitung zu 'boost :: variant' /' std :: variant' ist weise, aber das "nicht möglich" ist falsch.In jedem Fall hängt es nicht davon ab, ob Sie eine Klasse in sich selbst über einen 'std :: vector' verschachteln können. – Quentin

+0

ok. Ich bin neugierig, was mit Nicht-Trivialitäten funktioniert? Ich denke, es ist immer noch klug, std :: variant für Klassen zu verwenden, nicht wahr? –

5

Ich bezweifle, dass es wie erwartet funktioniert.

Sie haben Recht. Es wird nicht. Zuerst wird es nicht kompilieren, wenn Sie jemals versuchen, eine Node zu konstruieren. Sie werden so etwas wie erhalten:

prog.cc:26:10: error: call to implicitly-deleted default constructor of 'Node' 
    Node n; 
     ^

Das ist, weil in einem union, werden alle nicht-trivialen Operationen implizit gelöscht. Sie müssen ~Value() definieren, um das Richtige zu tun. Unter der Annahme type ist der Index, welches Element in der Union wir tatsächlich sind, um das einzuschalten und den entsprechenden Destruktor aufzurufen. Und dann das Gleiche zum Kopieren und Verschieben.

Das heißt, die Verschachtelung von unvollständigen Typen ist auch nicht in Ordnung. vector darf einen unvollständigen Typ haben, solange er vor der ersten Verwendung vollständig ist. Aber unordered_map hat diese Erlaubnis nicht. Sie müssen den Werttyp in etwas anderes einbetten - z. B. oder shared_ptr<Node>.

Was Sie in Value haben, ist ein gemeinsames Muster, das eine Million verschiedene Namen hat: diskriminierte Union, Sum Typ, oder am häufigsten variant. Anstatt das Rad neu zu erfinden, schlage ich vor, dass Sie statt dessen std::variant verwenden (wenn Sie einen neueren Compiler verwenden, um so etwas zu unterstützen) oder boost::variant (sonst). Damit haben Sie:

using Value = variant<int32_t, float, bool, 
    std::vector<Node>, std::unordered_map<std::string, Node>>; 

und diese Art ist bereits zerstörbar, kopierbar, beweglich und besuchbar für Sie.