2016-05-04 17 views
10

Ich versuche, eine vector, die string und int halten kann.So verwenden Sie eine Union von zwei Typen

Ich habe den Code unten versucht, aber ich habe den Kompilierungsfehler

error: use of deleted function 'my_union::~my_union()'

Was mache ich falsch?

#include <iostream> 
#include <vector> 

using namespace std; 

union my_union 
{ 
    string str; 
    int a; 
}; 

int main() 
{ 
    vector<my_union> v; 
    my_union u;   // error: use of deleted function 'my_union::~my_union()' 
    u.str = "foo"; 
    v.push_back(u); 
    return 0; 
} 
+0

Erneut geöffnet: C++ 11 erweitert erheblich, was in einer Union erlaubt ist, und 'std :: string', unter anderem, ist jetzt erlaubt. –

+1

@PeteBecker Ich bekomme das aber ist das nicht in [this] abgedeckt (http://stackoverflow.com/a/3521998/4342498) Antwort von der Q? – NathanOliver

+2

Wie planen Sie, zu wissen, welches Mitglied der Gewerkschaft später aus dem Vektor lesen soll? –

Antwort

14

Von here

If a union contains a non-static data member with a non-trivial special member function (default constructor, copy/move constructor, copy/move assignment, or destructor), that function is deleted by default in the union and needs to be defined explicitly by the programmer.

Sie müssen explizit eine destructor für Ihre Vereinigung definieren die eine automatisch für string gelöscht zu ersetzen.

Beachten Sie auch, dass dies nur in C++ 11 gültig ist. In früheren Versionen können Sie keinen Typ mit nicht-trivialen speziellen Member-Funktionen in einer Union haben.

Aus praktischer Sicht kann dies immer noch keine gute Idee sein.

+4

"kann ... nicht eine gute Idee sein" - in der Tat. Wenn Sie Typen mit nicht-trivialen Konstruktoren oder Destruktoren haben, müssen Sie neue und explizite Destruktoraufrufe platzieren, um ein Objekt eines anderen Typs in der Union zu speichern. –

+1

@CodesInChaos Ein Kommentar zu dieser Frage bezieht sich auf [boost :: variant] (http://www.boost.org/doc/libs/1_60_0/doc/html/variant.html). Ich habe es nicht selbst benutzt, aber von der Beschreibung auf der Webseite hört es sich genau an und gibt sogar genau die Situation in der Frage als Beispiel. – Rotem

1

Vor ++ 11 C war es nicht so zitiert here zu verwenden std::string in einer Gewerkschaft erlaubt:

Unions cannot contain a non-static data member with a non-trivial special member function (copy constructor, copy-assignment operator, or destructor).

Und da C 11 ++ Sie std::string in einer Vereinigung, wie durch @Rotem bereits beantwortet verwenden können, Sie brauchen eine destructor explizit Zeichenfolge zu definieren oder die destructor explicitly

str.~basic_string<char>(); 
2

rufen Wenn Sie eine Vereinigung mit einer Klasse, die im Grunde nicht nur alte Daten, in C++ ist 11 es Ihnen ermöglicht. Aber es geht und implizit löscht die meisten der speziellen Member-Funktionen wie der Destruktor.

union my_union 
{ 
    string str; 
    int a; 
}; 

das praktische Problem ist an der Stelle der Zerstörung C++, dass nicht kennt die der oben genannten Teile der Union gültig sind.

Sie können dies umgehen, indem Sie eine markierte Union verwenden und die aktive Spur beibehalten und in diesem Fall manuell die Zerstörung vornehmen.

So können wir so etwas wie erhalten:

struct tagged_union { 
    enum active {nothing, string, integer} which_active; 
    template<active...As> 
    using actives = std::integral_sequence<active, As...> 
    using my_actives = actives<nothing, string, integer>; 

    struct nothingness {}; 

    union my_union 
    { 
    nothingness nothing; 
    std::string str; 
    int a; 
    ~my_union() {}; 
    } data; 
    using my_tuple = std::tuple<nothingness, std::string, int>; 

    template<active which> 
    using get_type = std::tuple_element_t<(std::size_t)which, my_tuple>; 

    template<class F> 
    void operate_on(F&& f) { 
    operate_on_internal(my_actives{}, std::forward<F>(f)); 
    } 
    template<class T, class F> 
    decltype(auto) operate_on_by_type(F&& f) { 
    return std::forward<F>(f)(reinterpret_cast<T*>(&data)); 
    } 
    // const versions go here 
private: 
    // a small magic switch: 
    template<active...As, class F>  
    void operate_on_internal(actives<As...>, F&& f) { 
    using ptr = void(*)(my_union*,std::decay_t<F>*); 
    const ptr table[]={ 
     [](my_union* self, std::decay_t<F>* pf){ 
     std::forward<F>(*pf)(*(get_type<As>*)self); 
     }..., 
     nullptr 
    }; 
    table[which](&data, std::address_of(f)); 
    } 
public: 
    template<class...Args> 
    tagged_union(Active w, Args&&...args) { 
    operate_on([&](auto& t){ 
     using T = std::decay_t<decltype(t)>(); 
     ::new((void*)std::addressof(t)) T(std::forward<Args>(args)...); 
     which = w; 
    }); 
    } 
    tagged_union():tagged_union(nothing){} 

    ~tagged_union() { 
    operate_on([](auto& t){ 
     using T = std::decay_t<decltype(t)>(); 
     t->~T(); 
     which=nothing; 
     ::new((void*)std::addressof(t)) nothingness{}; // "leaks" but we don't care 
    }); 
    } 
}; 

, die im Grunde ist eine primitive Skizze, wie so etwas wie boost::variant funktioniert, wenn ++ 11 C geschrieben.

Es beinhaltet einige schwere mojo.

Das obige wurde nicht kompiliert, aber das Design ist solide. Einige nominal C++ 14-Compiler mögen es nicht, wenn ein Pack um ein volles Lambda erweitert wird, was jedoch noch mehr Standard erfordert.