【问题标题】:Why passing std::move(object) and member of this object to function causes SIGSEGV [duplicate]为什么将 std::move(object) 和该对象的成员传递给函数会导致 SIGSEGV [重复]
【发布时间】:2023-04-10 11:12:01
【问题描述】:

以下代码导致 SIGSEGV,我不明白为什么会这样。

#include <iostream>
using namespace std;

struct C {
    C(int x) { ptr = new int(x); }
    C(C&& c) { ptr = c.ptr; c.ptr = nullptr; }

    int* ptr;
};

void foo(int* x, C c) {
    cout << *x  << endl;
}

int main() {
    C c(10);
    foo(c.ptr, std::move(c));   
    return 0;
}

我希望指针 c.ptr 按值传递给函数 foo,但是它的行为就像它通过引用传递一样。

现在,如果我更改参数的顺序:void foo(C c, int* x),那么问题就会消失。 另一种解决方案是在调用 x 之前创建 c.ptr 的本地副本,然后将该本地副本传递给 foo。

我想了解为什么我不能在上面的示例代码中按值传递 c.ptr。

【问题讨论】:

  • 你搬到了一个临时的...

标签: c++ move segmentation-fault operator-precedence


【解决方案1】:

它是按值传递的,但是:

foo(c.ptr, std::move(c));

未指定参数that are passed to a function call get evaluated的顺序。

几乎所有 C++ 运算符的操作数的求值顺序 (包括函数参数的求值顺序 函数调用表达式 ... ) 未指定。

“未指定”表示可以按任何顺序评估它们。每次运行程序时,顺序甚至可能不同。您的编译器选择生成代码,首先使用std::move 评估第二个参数。因此,您的移动构造函数将指针移出对象,将其设置为空。然后,c.ptr 被求值,按值传递现在为空的指针。

【讨论】:

  • 有什么方法可以在移动之前强制评估第一个参数,它不会依赖编译器实现细节而不创建临时对象?
  • @Buyuk 您可以自己评估并存储结果。 int*x = c.ptr; foo(x, std::move(c));
  • @FrançoisAndrieux 我知道,但我想知道是否有办法在不创建临时对象(在本例中为指针)的情况下做到这一点,因为有时它可能很昂贵。
  • @Buyuk 不能强制函数参数的求值顺序。
  • 有时会错误地认为临时对象是昂贵的。通过仔细构造代码以使编译器清楚对象的生命周期是什么,现代编译器采用复杂的代码分析和优化算法,通常使编译器能够推断出对象的有限生命周期。因此使用与临时对象相同的移动语义,从而实现您想要的目标,但使用明确的评估顺序。
【解决方案2】:

当您调用foo(c.ptr, std::move(c)); 时,您不能确定c.ptrstd::move(c) 将首先被评估。未指定在函数调用中评估的顺序参数。在您的情况下,似乎将首先评估 std::move(c),将 c.ptr 保留为 nullptr。然后您执行cout &lt;&lt; *x &lt;&lt; endl 尝试取消引用*x,其中xnullptr

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 2013-05-18
    • 2015-03-04
    • 2021-08-17
    • 1970-01-01
    相关资源
    最近更新 更多