私はテンプレートを使用して、単純な型の共用体の構成を簡略化したいと考えています。以下は、実際には「仕事」に見えますが、仕様により、技術的、法的ではありません。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
?
template<typename T> struct union_entry {
void (*destructor_)(void *);
alignas(T) char value_[sizeof(T)];
}
- 6.2.1、std::is_standard_layout
はunion_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_
}
std :: aligned_storage <>を見てみるといいかもしれませんが、boost :: variant <>も見てみてください。恐らく、デストラクタポインタをユニオンの外側に置くことができるかもしれません。おそらく、すべてのメンバのために必要になるからです。 – Arvid