在 gcc 和 MSVC 中以不同方式调用函数参数的析构函数

2023-12-27

在将一些 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 Community 编译并运行上述代码,我会得到以下输出:

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

gcc 调用析构函数f,论证Bar(), after Bar()返回,而 msvc 在返回之前(显然)调用析构函数,或者至少在构造函数之前f2。什么时候f根据 C++ 标准,应该被破坏吗?


他们都很好;这取决于。标准中似乎未明确规定。

From [expr.call]/4 http://eel.is/c++draft/expr.call#4(这个措辞可以追溯到 C++98);

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

And the CWG#1880 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1880;

工作组决定不指定参数对象是在调用后立即销毁还是在调用所属的完整表达式末尾销毁。

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++ 都产生cXdMSVC 生产cdX.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 gcc 和 MSVC 中以不同方式调用函数参数的析构函数 的相关文章

随机推荐