std::future作为返回类型的未定义行为

Undefined behavior of std::future as return type?

本文关键字:未定义 返回类型 std future      更新时间:2023-10-16

在我的例子中,我使用std::future作为返回类型,但得到了未定义的行为。代码如下:

#include <future>
#include <iostream>
#include <pthread.h>
std::future<bool> update() {
  int c = 1; 
  //std::cout << "?" << c << std::endl; // debug line
  auto lambda = [&] () -> bool {
    int b = 0; 
    for(int i = 0; i < 10000000; ++i) {
      b += 1; 
    }  
    std::cout << "?" << c << std::endl;
    return c == 1; 
  }; 
  return std::async(std::launch::async, lambda);
}
int main(int argc, char *argv[])
{
  auto f2 = update();
  std::cout << f2.get() << std::endl;
  return 0; 
}

我用g++-4.7.2 -std=c++11 test_future.cc -lpthread编译了这段代码,得到了以下输出结果:

?0
0

但是当我取消注释上面的debug line时,输出变成了1(正如预期的那样)。此外,如果我用std::future作为参数,用update函数代替返回值,我也可以得到正确的输出结果。

我想知道这里出了什么问题。g++的bug还是用法的bug?谢谢

问题是通过引用捕获变量c,再加上异步运行lambda。最后一部分意味着update函数可能在lambda实际运行之前已经返回,现在它引用了一个不再存在的变量。

如果您有调试行,它似乎工作的原因是因为未定义的行为

因此,问题与您返回std::future无关。

显而易见的解决方案当然是按值捕获c,如果在实际代码中不可能,那么您可能需要重新考虑您的设计。

问题是您的lambda auto lambda = [&] () -> bool是通过引用捕获的。对c的引用可能是悬空的,因为线程可以在函数退出后执行。

您可能应该改为按值auto lambda = [=] () -> bool捕获。

对我来说,这看起来像是未定义的行为。您通过引用捕获c,但当lambda执行时,它已经超出了范围。

因此c == 1是未定义的行为,因为c已经超出了范围。如果您通过值捕获,您应该没事。

auto lambda = [c] () -> bool {
  int b = 0; 
  for(int i = 0; i < 10000000; ++i) {
    b += 1; 
  }  
  std::cout << "?" << c << std::endl;
  return c == 1; 
};