2016-05-04 8 views
10

Próbuję utworzyć , która może pomieścić string i int.Sposób korzystania z połączenia dwóch typów

Próbowałem poniższy kod, ale pojawia się błąd kompilacji

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

Co robię źle?

#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

Ponownie otwarte: C++ 11 znacznie rozszerzyło to, co jest dozwolone w związku, a między innymi "std :: string" jest teraz dozwolone. –

+1

@PeteBecker Dostaję to, ale czy nie jest to opisane w [this] (http://stackoverflow.com/a/3521998/4342498) odpowiedzi od Q? – NathanOliver

+2

W jaki sposób planujesz "wiedzieć", który członek związku będzie czytał później z wektora? –

Odpowiedz

14

Od 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.

Należy wyraźnie określić destruktor dla unii zastąpienia jednego automatycznie usunięty za string.

Należy również pamiętać, że jest to poprawne tylko w języku C++ 11. We wcześniejszych wersjach nie można mieć typu z nietrywialnymi specjalnymi funkcjami członków wewnątrz związku.

Z praktycznego punktu widzenia może to nadal nie być świetny pomysł.

+4

"może ... nie jest dobrym pomysłem" - w rzeczy samej. Kiedy masz typy z nietrywialnymi konstruktorami lub destruktorami, musisz użyć nowych i jawnych wywołań destruktora do umieszczenia obiektu innego typu w unii. –

+1

@CodesInChaos Komentarz do tego pytania nawiązuje do [boost :: variant] (http://www.boost.org/doc/libs/1_60_0/doc/html/variant.html). Nie używałem go sam, ale z opisu na stronie brzmi on na miejscu, nawet podając dokładnie sytuację w pytaniu jako przykład. – Rotem

1

Przed C++ 11 nie wolno było używać std::string w unii cytowany here:

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

A ponieważ C++ 11 można użyć std::string w unii jak już odpowiedział @Rotem, ty należy zdefiniować destruktora jawnie do sznurka lub zadzwoń destruktora explicitly

str.~basic_string<char>(); 
2

po utworzeniu unii z klasy, która nie jest w zasadzie zwykły stare dane, w C++ 11 to pozwala. Ale idzie i niejawnie usuwa większość specjalnych funkcji składowych, takich jak destruktor.

union my_union 
{ 
    string str; 
    int a; 
}; 

praktycznym problemem jest to, że w momencie zniszczenia C++ nie wiem który powyższych częściach Unii są ważne.

Możesz obejść ten problem, używając oznaczonego powiązania i śledzenia aktywnego, i ręcznie robiąc zniszczenie w tym przypadku.

Więc możemy dostać coś takiego:

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 
    }); 
    } 
}; 

, która jest w zasadzie prymitywny szkic tego, jak coś działa, jeśli boost::variant napisany w C++ 11.

Zawiera trochę ciężkiego mojo.

Powyższe nie zostało skompilowane, ale projekt jest dźwiękowy. Niektóre kompilatory C++ 14 nominalnie nie lubią robić paczek rozszerzać się wokół pełnej lambdy, która wymagałaby jeszcze większej ilości kart.

Powiązane problemy