2016-05-04 17 views
10

stringintを保持できるvectorを作ろうとしています。2種類の和集合を使用する方法

私は以下のコードを試してみたが、私は間違って何をやっている

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

コンパイルエラーを取得しますか?

#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

Reopened:C++ 11では、ユニオンで許可されているものが大幅に拡張され、他にも 'std :: string'などが許可されました。 –

+1

@PeteBecker私はそれを取得しますが、[this]でカバーされていません(http://stackoverflow.com/a/3521998/4342498)Qから答え? – NathanOliver

+2

どのようにして、後でベクターから読み込む組合のメンバーを「知る」ことを計画していますか? –

答えて

14

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.

から明示的に自動的にstringのために削除ものを置き換えるためにあなたの組合のためのデストラクタを定義する必要があります。

また、これはC++ 11でのみ有効です。以前のバージョンでは、ユニオン内に特別なメンバー関数を持たない型はまったくありません。

実用的な観点からは、これはまだ素晴らしい考えではありません。 C++ 11になる前に

+4

"良い考えにはならないかもしれない" - 確かに。重大ではないコンストラクタやデストラクタを持つ型がある場合は、配置の新しいデストラクタ呼び出しと明示的なデストラクタ呼び出しを使用して、異なる型のオブジェクトを共用体に格納する必要があります。 –

+1

@CodesInChaosこの質問に対するコメントは、[boost :: variant](http://www.boost.org/doc/libs/1_60_0/doc/html/variant.html)を参照しています。私はそれを自分で使ったことはありませんが、ウェブページ上の説明から、それは一見すると疑問の状況を正確に示していると言えます。 – Rotem

1

それはhereを引用として、労働組合にstd::stringを使用することを許されなかった:あなたは、

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

、すでに@Rotemによって答えとしてC++ 11以降に労働組合にstd::stringを使用することができます文字列を明示的にデストラクタを定義するか、またはあなたがC++ 11には、基本的には昔ながらのデータではないクラスで労働組合を作成する場合、それはあなたをすることができますexplicitly

str.~basic_string<char>(); 
2

デストラクタを呼び出す必要があります。しかし、それは暗黙のうちにデストラクタのような特別なメンバ関数の大部分を削除します。

union my_union 
{ 
    string str; 
    int a; 
}; 

実用上の問題は、破壊のCの時点で++組合の上記部品のが有効であるを知らないということです。

これを回避するには、タグ付きのユニオンを使用し、有効なトラックを維持し、その場合は手動で破壊を実行します。基本的にはC++ 11の中に書かれている場合どのようなもののようなboost::variant作品の原始的なスケッチである

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

だから我々のような何かを得ることができます。

重いモジョが関係します。

上記はコンパイルされていませんが、デザインは健全です。いくつかの名目上C++ 14コンパイラは、完全なラムダの周りで展開するのが好きではありませんが、それはさらに多くの定型文を必要とします。

関連する問題