返回值在局部變量超出範圍之前被複製出來。複製/移動可能是到臨時位置(堆棧或寄存器)或直接到調用者自己的緩衝區或首選寄存器 - 這是優化/內聯問題。
如果涉及臨時位置,編譯器必須在調用者和被調用者之間安排一些工作分工,並且有許多針對返回值的OS和二進制對象/可執行格式特定約定(以及當然的函數參數),這樣使用一個編譯器編譯的庫/對象通常可以與另一個編譯器一起使用。
請問線...
auto item = q.pop();
...是強烈的異常安全嗎?
假設pop_front()
不能throw
,有趣的情況是該函數的返回之後返回一個臨時位置,從該值被再次複製到呼叫者緩衝器。在我看來,你沒有充分保護這一點。 Elision(被調用者直接構造調用者結果緩衝區/寄存器中的返回值)是允許的,但不是必需的。
爲了探討這個問題,我已經寫了下面的代碼:
#include <iostream>
struct X
{
X() { std::cout << "X::X(this " << (void*)this << ")\n"; }
X(const X& rhs) { std::cout << "X::X(const X&, " << (void*)&rhs
<< ", this " << (void*)this << ")\n"; }
~X() { std::cout << "X::~X(this " << (void*)this << ")\n"; }
X& operator=(const X& rhs)
{ std::cout << "X::operator=(const X& " << (void*)&rhs
<< ", this " << (void*)this << ")\n"; return *this; }
};
struct Y
{
Y() { std::cout << "Y::Y(this " << (void*)this << ")\n"; }
~Y() { std::cout << "Y::~Y(this " << (void*)this << ")\n"; }
};
X f()
{
Y y;
std::cout << "f() creating an X...\n";
X x;
std::cout << "f() return x...\n";
return x;
};
int main()
{
std::cout << "creating X in main...\n";
X x;
std::cout << "x = f(); main...\n";
x = f();
}
與g++ -fno-elide-constructors
編譯,我的輸出(額外評論)是:
creating X in main...
X::X(this 0x22cd50)
x = f(); main...
Y::Y(this 0x22cc90)
f() creating an X...
X::X(this 0x22cc80)
f() return x...
X::X(const X&, 0x22cc80, this 0x22cd40) // copy-construct temporary
X::~X(this 0x22cc80) // f-local x leaves scope
Y::~Y(this 0x22cc90)
X::operator=(const X& 0x22cd40, this 0x22cd50) // from temporary to main's x
X::~X(this 0x22cd40)
X::~X(this 0x22cd50)
顯然,分配f()
發生後左範圍:任何異常將在您的範圍警衛(此處由Y代表)被銷燬後。
同類的事情發生,如果主要包含X x = f();
或X x(f());
,除了它的那是f()
- 本地變量的破壞後調用拷貝構造函數。 (我明白一個編譯器的行爲有時是推理標準是否需要工作的一個很差的基礎,但是反過來說它更可靠:當它不工作時,編譯器的破壞 - 這是相對較少的 - 或者標準並不要求它。在這裏,編譯器行爲只是用來增加我對標準要求的印象。)
爲好奇繁瑣的細節:不是,它是典型的有代碼,只能以一種方式被稱爲是有用的,但一些可能安全是const X& x = f();
,作爲const
參考延長臨時的使用壽命,但我無法說服自己,標準要求有臨時的生命週期的延長臨時功能複製到沒有任何額外的副本;因爲它的價值很小 - 它在我的程序中「起作用」,並且有趣的是,如果隱藏返回值,臨時佔用相同的堆棧位置,這暗示f()
代碼被有效編譯爲具有退出能力並且-f-no-elide-constructors
選項不是那麼多禁用作爲一種優化方式來加入悲觀情緒:在調用函數之前爲臨時方法留下額外的堆棧空間,然後添加額外的代碼以從中複製並破壞臨時的然後重新調整堆棧指針....
是的,在返回指令(及其構造/副本)後,具有自動存儲持續時間的變量(如您的'guard')將被銷燬。您可以查看跳轉指令和聲明的標準部分6.6和6.7。也許更容易,你可以嘗試用虛擬對象打印一些東西在銷燬^^ – lip