[英]Can I legally cast a pointer to a struct member to a pointer to the struct?
[英]Cast a pointer to struct to a pointer to the only member of that struct
考慮以下程序:
#include <algorithm>
#include <iostream>
#include <vector>
struct foo {
foo(int value)
: value_(value)
{
// perform range checks
}
int value() const {
return value_;
}
private:
int value_;
};
int main() {
std::vector<foo> values{0, 1, 2, 3, 4, 5};
std::for_each(std::begin(values), std::end(values),
[](foo& f){ std::cout << f.value(); });
std::cout << std::endl;
std::for_each(reinterpret_cast<const int*>(values.data()),
reinterpret_cast<const int*>(values.data()) + values.size(),
[](int i){ std::cout << i; });
}
用Apple LLVM 6.0版(clang-600.0.54)(基於LLVM 3.5svn)編譯后,它產生以下輸出(這正是我想要的):
012345
012345
第一次迭代是微不足道的。 然而,第二次迭代不是通過迭代器執行的,而是通過指向已經轉換為const int*
的底層存儲的指針執行的 。
我的問題是: 該代碼合法嗎?
我的直覺是它。 根據C ++ 11標准的§5.2.10/ 7(最終工作草案):
當“指向
T1
指針”類型的prvalue v轉換為“指向cvT2
指針”類型時,如果T1
和T2
都是標准布局,則結果為static_cast<cvT2*>(static_cast<cvvoid*>(v))
類型(3.9)和T2
的對齊要求不比T1
更嚴格
如果我正確解釋,那么上面的代碼應該是正確的,對嗎? 如果沒有,它可以成功嗎?
(在我的回答中,我使用C ++ 14標准草案(N4140),這與C ++ 11在相關引用方面略有不同)
由於[class.mem]/19
reinterpret_cast<const int*>(values.data())
[class.mem]/19
:
如果標准布局類對象具有任何非靜態數據成員,則其地址與其第一個非靜態數據成員的地址相同。 (...)[注意:因此,標准布局結構對象中可能存在未命名的填充,但不是在其開頭,以實現適當的對齊。 - 尾注]
關於解除引用, [expr.reinterpret.cast]/7
:
可以將對象指針顯式轉換為不同類型的對象指針。 當對象指針類型的prvalue v被轉換為對象指針類型“指向cv
T
指針”時,結果是static_cast<cv T*>(static_cast<cv void*>(v))
。
第一個static_cast
由[conv.ptr]/2
覆蓋:
類型為“指向cv
T
指針”的prvalue,其中T
是對象類型,可以轉換為類型為“指向cvvoid
指針”的prvalue。 將指針的非空指針值轉換為對象類型的結果為“指向cvvoid
指針”表示內存中與原始指針值相同的字節的地址。
第二個static_cast
- [expr.static.cast]/13
:
類型為“指向cv1 void的指針”的prvalue可以轉換為類型為“指向cv2 T的指針”的prvalue(...)如果原始指針值表示內存中字節的地址A並且A滿足對齊要求然后,結果指針值表示與原始指針值相同的地址,即A.
由於[class.mem]/19
,因此對齊要求得到滿足,所以演員表工作正常。
但問題是似乎不能保證sizeof(foo) == sizeof(int)
除了上面提到的std::complex
要求。 可以將[class.mem]/19
關於未命名填充的注釋解釋為僅在需要對齊時允許填充,因此在您的情況下不得有任何填充,但在我看來,這方面的注釋太模糊了。
您可以做的是放入您的代碼
static_assert(sizeof(foo) == sizeof(int), "");
// this may be paranoic but won't hurt
static_assert(alignof(foo) == alignof(int), "");
因此,如果違反要求,至少您的代碼將無法編譯。
它是正確的。 在某些條件下,指向結構的指針可以轉換為指向它的第一個成員的指針。 這是C的遺留保留,因為這是當時實現完全不繼承的方式。
這在C ++11§9.2/ 20 [class.mem]中指定:
指向標准布局結構對象的指針(使用
reinterpret_cast
適當轉換)指向其初始成員(或者如果該成員是位字段,則指向它所在的單元),反之亦然。 [ 注意 :因此,在標准布局結構對象中可能存在未命名的填充,但不是在其開頭,以實現適當的對齊。 - 結束說明 ]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.