gcc和MSVC中调用不同的函数参数的析构函数 在从微软Visual Studio到GCC移植一些C++代码时,我遇到了一个奇怪的bug,我最终将其归结为: #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))); }

gcc和MSVC中调用不同的函数参数的析构函数 在从微软Visual Studio到GCC移植一些C++代码时,我遇到了一个奇怪的bug,我最终将其归结为: #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))); },c++,c++11,gcc,visual-studio-2015,C++,C++11,Gcc,Visual Studio 2015,但是,如果我使用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 gcc在Bar()返回之后调用f的析构函数,Bar()的参数,而msvc在它返回之前调用析构函数(显然),或者至少在f2的构造

但是,如果我使用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

gcc在
Bar()
返回之后调用
f
的析构函数,
Bar()
的参数,而msvc在它返回之前调用析构函数(显然),或者至少在
f2
的构造函数之前调用析构函数。当代码> f < /> >被破坏时,根据C++标准?< /p> 它们都是正确的;视情况而定。标准中似乎没有明确规定

From(这个措辞可以追溯到C++98)

当定义参数的函数返回时,参数的生存期结束。每个参数的初始化和销毁都发生在调用函数的上下文中

以及

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;
}
#包括
结构参数{

Arg(){std::cout
f
,作为
Bar()
的命名参数,不是临时的。事实上,我没有看到任何临时的。另外两个
Foo
被命名为f1和f2。不确定这里是否有实际问题。我可能对什么是临时的感到困惑。我的问题是
Bar()的
f
参数是什么时候的
应该被销毁,哪个实现,msvc还是gcc,是正确的?@MSalters我不认为这是该问题的重复。这是关于
f(a,b,c)中的构造/销毁顺序
但是这个问题从不调用超过1的函数argument@M.M:同时检查答案。这个问题是链接问题中更一般问题的一个简单版本。好的,所以这个问题的答案是在C++14中,在
Bar(Foo f)
中的
f
必须在构建
f2
之前销毁(所以g++是错误的),但是CWG决定改变这一点(也许是为了回应编译器的错误)。
#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;
}