[英]Does encapsulated char array used as object breaks strict aliasing rule
以下類是否違反嚴格別名規則:
template<typename T>
class store {
char m_data[sizeof(T)];
bool m_init;
public:
store() : m_init(false) {}
store(const T &t) : init(true) {
new(m_data) T(t);
}
~store() {
if(m_init) {
get()->~T();
}
}
store &operator=(const store &s) {
if(m_init) {
get()->~T();
}
if(s.m_init) {
new(m_data) T(*s.get());
}
m_init = s.m_init;
}
T *get() {
if (m_init) {
return reinterpret_cast<T *>(m_data);
} else {
return NULL;
}
}
}
我對標准的解讀是它不正確但我不確定(我的用法是擁有一個對象數組T
+這些對象的一些元數據,但是可以控制對象構造/解構而無需手動分配內存)作為已分配對象用作標准中new
放置的示例。
該標准包含此注釋:
[ 注意:典型的實現會將aligned_storage定義為:
template <std::size_t Len, std::size_t Alignment> struct aligned_storage { typedef struct { alignas(Alignment) unsigned char __data[Len]; } type; };
- 結束說明 ]
- 指針修改[meta.trans.ptr] 20.9.7.5/1
align_storage的部分定義為:
成員typedef 類型應為POD類型,適合用作任何大小最多為Len且其對齊方式為Align的除數的對象的未初始化存儲。
限制可以構造對象的地址的標准所涵蓋的唯一屬性是對齊。 實現可能有一些其他限制,但我不熟悉任何其他限制。 因此,只需確保正確對齊就足夠了,我認為這應該沒問題。 (在前C ++ 11編譯器中,您可以使用編譯器擴展來設置對齊,例如__attribute__((alignment(X)))
或__declspec(align(X))
。
我相信只要你不直接訪問底層存儲,別名規則就不會出現在圖片中,因為別名規則涵蓋了可以通過不同類型的對象訪問對象的值。 構造對象並僅訪問該對象不涉及通過任何其他類型的對象訪問對象的值。
別名規則專門允許char數組為其他對象設置別名。
如果程序試圖通過以下類型之一以外的glvalue訪問對象的存儲值,則行為未定義:
[...]
- char或unsigned char類型。
- 左值和右值[basic.lval] 3.10 / 10
您確實需要確保數組正確對齊T類型。
alignas(T) char m_data[sizeof(T)];
以上是用於設置對齊的C ++ 11語法,但如果您使用的是C ++ 03編譯器,那么您將需要一個特定於編譯器的屬性來執行相同的操作。 GCC有__attribute__((aligned(32)))
,MSVC有__declspec(align(32))
Kerrek SB提出了一個很好的觀點,即別名規則表明可以通過char數組訪問T對象的值,但這可能並不意味着通過T對象訪問char數組的值是可以的。 但是,如果放置新表達式定義良好,那么創建一個T對象,我認為可以按定義訪問作為T對象,並且讀取原始char數組正在訪問創建的T對象的值,該對象包含在別名規則。
我認為這意味着您可以將T對象存儲在例如int數組中,並且只要您不通過原始int數組訪問該T對象的值,那么您就不會遇到未定義的行為。
允許的是獲取一個T
對象並將其解釋為一個字符數組。 但是,它是在一般不允許參加字符的任意陣列,並且把它作為一個T
,或甚至作為指針的含有存儲器的區域T
。 至少,你的char數組需要正確對齊。
解決這個問題的一種方法可能是使用聯合:
union storage { char buf[sizeof(T)]; T dummy; };
現在你可以在storage.buf
構建一個T
:
T * p = ::new (storage.buf) T();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.