![](/img/trans.png)
[英]Sequencing between the destruction of local automatic function variables vs the construction of the return value
[英]Sequencing of function parameter destruction
根據 C++14 [expr.call]/4:
當定義它的函數返回時,參數的生命周期結束。
這似乎意味着參數的析構函數必須在調用該函數的代碼繼續使用該函數的返回值之前運行。
但是,此代碼顯示不同:
#include <iostream>
struct G
{
G(int): moved(0) { std::cout << "G(int)\n"; }
G(G&&): moved(1) { std::cout << "G(G&&)\n"; }
~G() { std::cout << (moved ? "~G(G&&)\n" : "~G()\n"); }
int moved;
};
struct F
{
F(int) { std::cout << "F(int)\n"; }
~F() { std::cout << "~F()\n"; }
};
int func(G gparm)
{
std::cout << "---- In func.\n";
return 0;
}
int main()
{
F v { func(0) };
std::cout << "---- End of main.\n";
return 0;
}
帶有-fno-elide-constructors
gcc 和 clang 的輸出是(帶有我的注釋):
G(int) // Temporary used to copy-initialize gparm
G(G&&) // gparm
---- In func.
F(int) // v
~G(G&&) // gparm
~G() // Temporary used to copy-initialize gparm
---- End of main.
~F() // v
因此,顯然v
的構造函數在gparm
的析構函數之前運行。 但是在 MSVC 中, gparm
在v
的構造函數運行之前被銷毀。
啟用復制省略和/或使用func({0})
可以看到相同的問題,以便直接初始化參數。 v
總是在gparm
被破壞之前被構造。 我也在更長的鏈中觀察到這個問題,例如F v = f(g(h(i(j())));
直到v
被初始化后才破壞f,g,h,i
任何參數。
這在實踐中可能是一個問題,例如,如果~G
解鎖資源並且F()
獲取資源,這將是一個死鎖。 或者,如果~G
拋出,那么執行應該跳轉到一個 catch 處理程序,而v
沒有被初始化。
我的問題是:標准是否允許這兩種排序? . 除了 expr.call/4 中不使用標准排序術語的引用之外,是否有更具體的定義涉及參數破壞的排序關系?
其實我可以回答我自己的問題......在寫之前搜索時沒有找到答案,但后來再次搜索確實找到了答案(典型的哈)。
無論如何:這個問題是CWG #1880 ,有以下注釋:
2014 年 6 月會議記錄:
WG 決定不指定參數對象是在調用之后立即銷毀還是在調用所屬的完整表達式結束時銷毀。
盡管問題 1880 仍然開放。
P0135也訪問了該主題- 保證復制省略,使其實現定義,而不是未指定。 在 C++17 (N4659) 中,文本是:
參數的生命周期是在定義它的函數返回時結束還是在封閉的完整表達式結束時結束,這是實現定義的。
這里有更多背景信息:函數參數的后期破壞
注意:全表達式的定義可以在 C++14 [intro.execution]/10 中找到:
完整表達式是不是另一個表達式的子表達式的表達式。 [...] 如果語言結構被定義為產生一個函數的隱式調用,那么為了這個定義的目的,語言結構的使用被認為是一個表達式。
所以F v { func(0) };
是gparm
的封閉完整表達式(即使它是聲明而不是表達式!)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.