2017-09-13 48 views
0

我對在VC++ 2015中看到的結果感到驚訝,並且需要幫助瞭解它是如何工作的。如何從void *的unique_ptr <T>轉換爲T **?

struct MyType 
{ 
    MyType(int x_) : x(x_) { } 
    int x; 
}; 

auto u = std::make_unique<MyType>(10); 
void* pv = &u; 

這顯然失敗了,因爲u的地址不是一個指向MyType

MyType *pM = (MyType*)pv; 

但是這個作品,pM2獲取存儲在uMyType對象的地址:

MyType** ppM = (MyType**)pv; 
MyType* pM2 = *ppM; 

標準中有沒有什麼說這個應該起作用?還是隻是因爲編譯器的非可移植實現細節才起作用?有什麼東西可以讓我把unique_ptr當作指針指向一個圓的方向?

在你說「這很愚蠢,不要使用void*或C風格轉換」之前,請理解我正在處理遺留代碼,它通過void指針和結構成員的偏移來處理結構的序列化。我現在無法改變那部分。但是我想爲結構成員使用unique_ptr來簡化內存所有權和清理。我想知道我的unique_ptr在這個傳統環境中有多脆弱。

+0

爲什麼不只是'MyType * pv = u.get();'? http://en.cppreference.com/w/cpp/memory/unique_ptr/get – Brandon

+0

你的意思是*作品/不工作*爲上述每一個?你是否試圖在每種情況下獲得'unique_ptr'擁有的對象的指針?如果是的話,那麼'MyType **'會投出什麼?只需使用'void * pv = u.get();''unique_ptr''具有無狀態的刪除器,您的操作通常只包含一個指向託管對象的指針,這就是爲什麼您的類型雙擊似乎工作。 – Praetorian

+0

你真是幸運,因爲'std :: unique_ptr'可能是一個帶有單個指針屬性的結構,而且它恰好是'std :: unique_ptr'類型的對象的地址與第一個屬性的地址相同這個對象,它是指針。 – Holt

回答

2

這基本上只是你幸運。

在您的特定編譯器的ABI中,存儲由unique_ptr維護的對象的T*是對象的第一個成員,因此它具有與對象本身相同的地址。在幾乎相同的方式爲這個例子:

struct container { 
    int val; 
}; 

int main() { 
    container c{15}; 

    intptr_t val1 = reinterpret_cast<intptr_t>(&c); 
    intptr_t val2 = reinterpret_cast<intptr_t>(&(c.val)); 

    assert(val1 == val2); //will pretty much always be true 
} 

當然,這不是你應該依賴於行爲!標準沒有指定它,如果供應商決定在std::unique_ptr內存儲指針的格式更好,它可能會改變。

0

這是未定義的行爲,因爲唯一指針恰好只存儲單個指針作爲其狀態,而且該狀態是指向T的指針。

未定義的行爲可以執行任何操作,包括時間旅行和格式化硬盤驅動器。我知道人們說這個,其他人認爲這是一個笑話,但這些實際上是你可以通過實驗驗證的真實陳述。

碰巧,您未定義的行爲已經以「有效」的方式重新解釋了一些內存。

您無法使用您的庫以定義的方式序列化/反序列化非pod結構。你可以破解它的工作,但任何編譯器更新(甚至編譯器標誌更新!)可能突然表現完全不同。

考慮有一個序列化/反序列化的結構,另一個用於運行時使用。馬歇爾從一個到另一個。是的,這很糟糕。

+0

我的印象是這裏的行爲是*未指定*,而不是*未定義*。 – Xirema

+0

@Xirema取消引用不是該對象的對象的指針?這觸發了未定義的行爲。我想一個特殊的獨特的ptr實現可能是標準佈局,前綴與'T *'兼容,並且由編譯器的標準庫保證是這樣的。 – Yakk

1

本質上你正在做這樣的事情:

std::unique_ptr<MyType> up = ...; 
MyType* p = *reinterpret_cast<MyType**>(&up); 

隨着一些彎路和C風格的轉換。您將指針指向unique_ptr並將其重新解釋爲指向指針的指針MyType

這是純粹的運氣,會導致未定義的行爲,您不應該因爲任何原因使用此類代碼。如果您需要內部指針,請在unique_ptr上使用get()方法。

+0

你說得對。我在幾個在線編譯器上測試了我的SSCCE,他們的表現與我的VS2015完全一樣,但最終它是UB,應該避免。 – Matthew

相關問題