簡體   English   中英

將指向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轉換為“指向cv T2指針”類型時,如果T1T2都是標准布局,則結果為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是對象類型,可以轉換為類型為“指向cv void指針”的prvalue。 將指針的非空指針值轉換為對象類型的結果為“指向cv void指針”表示內存中與原始指針值相同的字節的地址。

第二個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM