我想使用模板來簡化非平凡類型的工會的建設。下面,似乎在實踐中「工作」,但在技術上是不合法的規範: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
?因此可以將value_
轉換爲T&
來模擬前面的示例,但仍允許destructor_
用於非活動union_entry
?
template<typename T> struct union_entry {
void (*destructor_)(void *);
alignas(T) char value_[sizeof(T)];
}
在兩種鐺-3.8.1和g ++ - 6.2.1,std::is_standard_layout
表明union_entry<T>
是即使當T
不是標準的佈局。下面是我想如何使用這種技術的完整工作示例:
#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