2016-10-12 4 views
3

私はテンプレートを使用して、単純な型の共用体の構成を簡略化したいと考えています。以下は、実際には「仕事」に見えますが、仕様により、技術的、法的ではありません。C++ 14の標準レイアウトタイプはフィールドに `alignas`を使用できますか?

template<typename T> struct union_entry { 
    void (*destructor_)(void *); // how to destroy type T when active 
    T value_; 
}; 
union U { 
    union_entry<A> a; 
    union_entry<B> b; 
    // ... some constructor and destructor... 
}; 

問題は、あなたが労働組合に二つの構造の共通の初期シーケンスにアクセスすることができます(N4141による)ということである(つまり、 、destructor_フィールド)、両方の構造体が標準レイアウト型である場合にのみ - 少なくとも9.5.1の非規範的な注釈に従って。 9.0.7によれば、標準レイアウトタイプは非標準レイアウトの非静的データメンバーを持つことができません。したがって、AまたはBのいずれかが標準レイアウトでない場合、間違った組合のdestructor_にアクセスすることは違法になります。

抜け穴value_alignas(T) char[sizeof(T)]にすると、抜け穴がunion_entryになるようです。 9.0.7のどの部分も、alignasの使用を除外していません。したがって、私の質問:タイプTの次の標準レイアウトタイプですか?したがって、をT&にキャストして前の例をエミュレートすることができますが、まだを非アクティブで使用することができますunion_entry

打ち鳴らす-3.8.1およびg ++の両方において
template<typename T> struct union_entry { 
    void (*destructor_)(void *); 
    alignas(T) char value_[sizeof(T)]; 
} 

- 6.2.1、std::is_standard_layoutunion_entry<T>Tがない場合であっても、標準的なレイアウトであることを示唆しています。ここで私はこのテクニックを使用したい方法の完全な作業例があります:std::aligned_storageに私を指摘@Arvid、へ

#include <cassert> 
#include <iostream> 
#include <new> 
#include <string> 

using namespace std; 

template<typename T> struct union_entry { 
    void (*destructor_)(void *); 
    alignas(T) char value_[sizeof(T)]; 

    union_entry() : destructor_(nullptr) {} 
    ~union_entry() {} // Just to cause error in unions w/o destructors 

    void select() { 
    if (destructor_) 
     destructor_(this); 
    destructor_ = destroy_helper; 
    new (static_cast<void *>(value_)) T{}; 
    } 
    T &get() { 
    assert(destructor_ == destroy_helper); 
    return *reinterpret_cast<T *>(value_); 
    } 

private: 
    static void destroy_helper(void *_p) { 
    union_entry *p = static_cast<union_entry *>(_p); 
    p->get().~T(); 
    p->destructor_ = nullptr; 
    } 
}; 

union U { 
    union_entry<int> i; 
    union_entry<string> s; 
    U() : i() {} 
    ~U() { if (i.destructor_) i.destructor_(this); } 
}; 

int 
main() 
{ 
    U u; 
    u.i.select(); 
    u.i.get() = 5; 
    cout << u.i.get() << endl; 
    u.s.select(); 
    u.s.get() = "hello"; 
    cout << u.s.get() << endl; 
    // Notice that the string in u.s is destroyed by calling 
    // u.i.destructor_, not u.s.destructor_ 
} 
+1

std :: aligned_storage <>を見てみるといいかもしれませんが、boost :: variant <>も見てみてください。恐らく、デストラクタポインタをユニオンの外側に置くことができるかもしれません。おそらく、すべてのメンバのために必要になるからです。 – Arvid

答えて

0

おかげで、私はセクション20.10で(非規範的ではあるが)決定的な答えがあると考えています。 7.6の標準(これはN4141と同じであると仮定します)。

まず、表57は、9.0.10を明らかにし、「POD構造体は、些細な両方である非組合クラスである「type typedefのメンバーは、PODタイプ ...しなければならない」aligned_storageの言いますクラスと標準レイアウトクラスです。

次に、20.10.7.6.1は非規範的実装例を示します:

template <std::size_t Len, std::size_t Alignment> 
struct aligned_storage { 
    typedef struct { 
    alignas(Alignment) unsigned char __data[Len]; 
    } type; 
}; 

だから、はっきりalignasの使用は、標準的なレイアウトであることから、種類を防ぐことはできません。

関連する問題