传给std::async的变量离开作用域后代码是否合法?
std::async传递std::future的作用域合法性问题
请问如下代码片段是否合法?顾虑点为:当factorial函数被异步调用时,块作用域内定义的fut_num可能已经离开作用域被销毁。
第一份测试代码
#include <future> #include <vector> #include <iostream> // int factorial(std::future<int> fut) // 可正常运行,因std::future存在移动构造函数 int factorial(std::future<int>&& fut) { int res = 1; int num = fut.get(); for(int i = num; i > 1; i--) { res *= i; } return res; } int main() { std::promise<int> prs; std::vector<std::future<int>> vec; { std::future<int> fut_num{prs.get_future()}; vec.push_back(std::async(std::launch::async, factorial, std::move(fut_num))); } // fut_num在此处离开作用域 prs.set_value(5); for(auto& fut: vec) { std::cout << fut.get() << std::endl; } }
第二份相似测试代码
#include <future> #include <vector> #include <iostream> // int factorial(std::future<int> fut) // 可正常运行,因std::future存在移动构造函数 int factorial(std::future<int>& fut) { int res = 1; int num = fut.get(); for(int i = num; i > 1; i--) { res *= i; } return res; } int main() { std::promise<int> prs; std::vector<std::future<int>> vec; { std::future<int> fut_num{prs.get_future()}; vec.push_back(std::async(std::launch::async, factorial, std::ref(fut_num))); } // fut_num在此处离开作用域 prs.set_value(5); for(auto& fut: vec) { std::cout << fut.get() << std::endl; } }
合法性判断结论
- 第一份代码完全合法:
std::async会将传入的所有参数按值存储在自身的内部共享状态中,此处传入std::move(fut_num)会触发std::future的移动构造,生成一个独立的std::future实例,生命周期归async的内部状态管理,和块作用域内的原fut_num对象完全解耦。即使原fut_num在块结束时被销毁,async持有的移动后实例依然有效,函数的右值引用参数可以正常绑定到该内部实例,后续调用get()不存在悬空引用问题。 - 第二份代码属于未定义行为,不合法:此处通过
std::ref(fut_num)传入的是原块作用域内fut_num对象的左值引用,std::async内部仅存储该引用,不会拷贝或移动future对象本身。当块作用域结束后,原fut_num被销毁,后续异步线程调用factorial访问该引用时,引用已经悬空,属于典型的对象生命周期错误。
补充说明:如果将
factorial的参数改为按值接收std::future<int> fut,传入参数时会触发移动构造生成独立的future实例,不会出现上述生命周期问题。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

