以下代碼洗掉警告 C4172:使用 MSVC 在 Windows 下回傳區域變數或臨時變數的地址。但我想知道在這種情況下它是否是一個真正的錯誤?我知道這里有很多類似的主題,我從這個警告中閱讀了很多類似的主題。所以在這種情況下,回傳值是一個來自“main”函式的指標,它應該一直存在到程式結束。如果returningLocalPointer 會回傳:“A something; return &something;” 那么是的,這將是一個問題,但在這種情況下,我們回傳一個存在直到“main”結束的指標。還是我錯了?
class A
{
};
A* returningLocalPointer(A* a)
{
return a;
}
template<typename T>
T const& doWarning(T const& b)
{
A* c = returningLocalPointer(b);
return c; // error if uses call-by-value
}
int main()
{
A d;
auto m = doWarning(&d); //run-time ERROR
}
uj5u.com熱心網友回復:
是的,這是一個真正的問題。您的程式的行為未定義。 c是與 參考的指標不同的物件b,其生命周期在 結??束時結束doWarning。這兩個指標指向同一個A物件 ( d),但這并不意味著它們是同一個物件。
為了說明這一點,我將或多或少地逐行并使用圖表:
A d;
auto m = doWarning(&d);
這將創建一個A名為的物件d并將指向該物件的匿名指標傳遞給doWarning。我稍后會m講到,但現在正在播放的物件如下所示:
d
┌─────┐ ┌─────┐
│ │ │ │
│ A* ├──────?│ A │
│ │ │ │
└─────┘ └─────┘
template<typename T>
T const& doWarning(T const& b)
{
在這里,T將被推匯出為A*,因為那是傳遞給它的。
doWarning通過參考接受其引數,因此 的型別b將為A* const &。也就是說,b是對指向dfrom的匿名指標的參考main:
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────?│ A* ├──────?│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
A* c = returningLocalPointer(b);
在這里,您創建了另一個指標 ,c它指向與 相同的物件b。我不會看returningLocalPointer,因為它或多或少無關緊要。這條線可以被替換,A* c = b;什么都不會改變。您的物件現在如下所示:
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────?│ A* ├──────?│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
▲
c │
┌─────┐ │
│ │ │
│ A* ├──────────┘
│ │
└─────┘
如您所見,c是與 參考的物件不同的物件b。
return c;
由于doWarning回傳 an A* const&(since Tis A*),這將初始化回傳值以參考區域變數c:
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────?│ A* ├──────?│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
▲
return value c │
┌───────────┐ ┌─────┐ │
│ │ │ │ │
│ A* const& ├──────?│ A* ├──────────┘
│ │ │ │
└───────────┘ └─────┘
}
現在doWarning結束,因此它的區域變數c超出范圍并且它的生命周期結束。這使doWarning的回傳值懸而未決:
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────?│ A* ├──────?│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
return value
┌───────────┐
│ │
│ A* const& ├──────? Nothing here anymore
│ │
└───────────┘
auto m = doWarning(&d);
現在我們回到m. auto本身永遠不會推斷出參考型別,因此m推斷出的型別為A*。這意味著程式將嘗試復制doWarning回傳的參考所參考的指標。doWarning參考的回傳值的指標不再存在。試圖復制一個不存在的物件是一個錯誤,如果一個程式這樣做,它的行為是未定義的。
uj5u.com熱心網友回復:
讓我們“實體化”這個函式T = A*(就像你用 呼叫它時那樣doWarning(&d)):
template
A* const& doWarning<A*>(A* const& b)
{
A* c = returningLocalPointer(&b);
return c;
}
您也許能夠看到問題出在哪里。c通過參考回傳,但它是一個立即銷毀的區域變數,因此doWarning將始終回傳一個懸空參考。
MSVC 似乎對指向本地的指標和指向本地的參考使用相同的警告,這就是為什么它在真正涉及參考時談論地址。GCC 警告可能更清楚:
In instantiation of 'const T& doWarning(const T&) [with T = A*]':
warning: reference to local variable 'c' returned [-Wreturn-local-addr]
return c; // error if uses call-by-value
^
note: declared here
A* c = returningLocalPointer(b);
^
uj5u.com熱心網友回復:
您正在回傳對區域變數的參考,c因此您的代碼具有未定義的行為。你可能會“走運”,m最終會成為一個指標,d但不能保證。
此代碼將定義行為,因為它回傳對的參考d而不是對的參考,c但如果doWarning使用臨時值呼叫它,它仍將具有未定義的行為(因此可能仍會產生警告):
A* returningLocalPointer(A* a)
{
return a;
}
template<typename T>
T const& doWarning(T const& b)
{
A* c = returningLocalPointer(&b);
return *c;
}
int main()
{
A d;
auto& m = doWarning(d);
// Undefined behaviour
auto& n = doWarning(A{});
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/394722.html
上一篇:設定沒有設定器的模擬類的欄位
下一篇:我在C中的結構鏈表有問題
