将右值传递给 std::move 并将右值引用绑定到它会在 ASAN 中生成 stack-use-after-scope 错误

zxy*_*122 4 c++ stdmove

考虑这个代码片段

#include <iostream>
int foo() {
  int a;
  return a;
}
int main() {
  auto &&ret = std::move(foo());
  std::cout << ret;
}
Run Code Online (Sandbox Code Playgroud)

使用ASAN编译g++ -fsanitize=address -fno-omit-frame-pointer -g -std=c++17 main.cpp -o main,运行./main显示错误

==57382==ERROR: AddressSanitizer: stack-use-after-scope on address 0x00016b3cb480 at pc 0x000104a37e68 bp 0x00016b3cb450 sp 0x00016b3cb448
READ of size 4 at 0x00016b3cb480 thread T0
    #0 0x104a37e64 in main main.cpp:8
    #1 0x104a75084 in start+0x200 (dyld:arm64e+0x5084)
Run Code Online (Sandbox Code Playgroud)

但是如果我在 auto 之后删除引用,这段代码可以编译并运行,而不会出现 ASAN 给出的错误。我不明白的是,如果std::move返回对给定对象的引用,那么该对象(foo()在本例中是临时创建的)将在函数调用返回后被销毁std::move,因此无论它是绑定到右值引用还是分配给新值应该无效,因为该临时值在移动操作后已经被销毁,对吗?那么为什么 ASAN 在第二种情况下不会给出错误。

- - - - - - - - - - -更新 - - - - - - - -

 #include <iostream>
    int foo() {
      int a = 1;
      return a;
    }
    int main() {
      auto &&ret = std::move(foo());
      std::cout << ret;
    }
Run Code Online (Sandbox Code Playgroud)

a如果我不使用 ASAN 进行编译,此代码实际上将运行而不会出现错误,它不会触发分段错误或任何内容,并打印 1 ,这与函数中的相同foo。从下面的答案中我了解到临时被销毁了,对auto &&ret = std::move(foo());吧?这是未定义的行为吗?

Dre*_*ann 9

将纯右值(例如)分配intauto&&变量将启用生命周期延长。只要变量存在,临时变量就会存在。

传递纯右值std::move()会将纯右值 ( int) 转换为xvalue ( int&&)。生命周期延长不再适用,并且临时在到达之前被销毁std::cout << ret;

这就是为什么 your 的存在std::move会触发 stack-use-after-scope 的原因。此举阻碍了寿命的延长。

教训:您可能永远不想将纯右值(临时值)传递给std::move.


app*_*ple 6

临时的仅在完整表达后才被销毁。(初始化后ret

// 1. the temporary is created and bind to std::move parameter
// 2. std::move return reference to the temporary
// 3. it's *value* copied to ret
// 4. temporary is destoryed, but it'd not effect the copy (`ret`)
auto ret = std::move(foo());
Run Code Online (Sandbox Code Playgroud)
// 1. the temporary is created and bind to std::move parameter
// 2. std::move return reference to the temporary
// 3. the *reference* is set to `ret`
// 4. temporary is destoryed, `ret` now contains dangling reference
auto&& ret = std::move(foo());
Run Code Online (Sandbox Code Playgroud)