[英]Destructor of a function argument being called differently in gcc and MSVC
在將一些C ++代碼從Microsoft Visual Studio移植到gcc時,我遇到了一個奇怪的錯誤,我最終歸結為:
#include <iostream>
using namespace std;
class Foo {
public:
int data;
Foo(int i) : data(i)
{
cout << "Foo constructed with " << i << endl;
}
Foo(const Foo& f) : data(f.data)
{
cout << "copy ctor " << endl;
}
Foo(const Foo&& f) : data(f.data)
{
cout << "move ctor" << endl;
}
~Foo()
{
cout << "Foo destructed with " << data << endl;
}
};
int Bar(Foo f)
{
cout << "f.data = " << f.data << endl;
return f.data * 2;
}
int main()
{
Foo f1(10);
Foo f2(Bar(std::move(f1)));
}
如果我使用Microsoft Visual Studio 2015社區編譯並運行上面的代碼,我會得到以下輸出:
Foo constructed with 10
move ctor
f.data = 10
Foo destructed with 10
Foo constructed with 20
Foo destructed with 20
Foo destructed with 10
但是,如果我使用gcc 6.1.1和--std = c ++ 14編譯並運行代碼,我會得到以下輸出:
Foo constructed with 10
move ctor
f.data = 10
Foo constructed with 20
Foo destructed with 10
Foo destructed with 20
Foo destructed with 10
在Bar()
返回之后,gcc調用f
的析構f
, Bar()
的參數,而msvc在返回之前調用析構函數(顯然),或者至少在f2
的構造函數之前調用析構函數。 當f
應該被破壞,根據C ++標准?
他們沒事; 這取決於。 標准似乎沒有具體說明。
來自[expr.call] / 4 (這個措辭可以追溯到C ++ 98);
參數的生命周期在定義它的函數返回時結束。 每個參數的初始化和銷毀發生在調用函數的上下文中。
而CWG#1880 ;
WG決定不確定參數對象是在調用之后立即銷毀還是在調用所屬的完整表達式結束時銷毀。
g ++(和clang)和MSVC的行為都是正確的,實現可以自由選擇一種方法而不是另一種方法。
這就是說,如果您擁有的代碼依賴於這種排序,我會改變它以使排序更具確定性 - 正如您所見,它會導致細微的錯誤。
這種行為的簡化示例是;
#include <iostream>
struct Arg {
Arg() {std::cout << 'c';}
~Arg() {std::cout << 'd';}
Arg(Arg const&) {std::cout << 'a';}
Arg(Arg&&) {std::cout << 'b';}
Arg& operator=(Arg const&) {std::cout << 'e'; return *this;}
Arg& operator=(Arg&&) {std::cout << 'f'; return *this;}
};
void func(Arg) {}
int main() {
(func(Arg{}), std::cout << 'X');
std::cout << std::endl;
}
Clang和g ++都產生cXd
和MSVC產生cdX
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.