为什么将 std::move(object) 和此对象的成员传递给函数会导致 SIGSEGV

Why passing std::move(object) and member of this object to function causes SIGSEGV

以下代码生成 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。

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

它是按值传递的,但是:

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

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

Order of evaluation of the operands of almost all C++ operators (including the order of evaluation of function arguments in a function-call expression ... ) is unspecified.

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