2016-10-12 27 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因此可以將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_ 
} 
+1

你可能想看看std :: aligned_storage <>,但是,你也許想看看boost :: variant <>。也許你可以把析構函數指針放在聯合之外,因爲你可能需要每個成員都需要它。 – Arvid

回答

0

感謝@Arvid,誰向我指出std::aligned_storage,我相信這是一個明確的(雖然非規範)答案在第20.10。該標準的7.6(我認爲它與N4141相同)。

首先,表57的說:aligned_storage「的typedef type 成員應是一個POD類型 ...」,其中9.0.10明確「一個POD結構是一個非工會類,既是平凡類別和標準佈局類別。「

接下來,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不會阻止類型被標準佈局。

相關問題