使用`std::future`的右值引用作为参数是否合法?

3

使用 std::future 的右值引用作为参数是否合法?由于 std::future::get() 未标记为 const,是否存在潜在问题需要注意?

如果我有遗漏,请告诉我。

这是完整的代码片段

#include <future>
#include <vector>
#include <iostream>

//int factorial(std::future<int> fut) //works, because there is a move constructor
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::future<int> fut_num{prs.get_future()};
    std::vector<std::future<int>> vec;

    vec.push_back(std::async(std::launch::async, factorial, std::move(fut_num)));
    

    prs.set_value(5);

    for(auto& fut: vec)
    {
        std::cout << fut.get() << std::endl;
    }
}

我知道如果传递左值引用,事情会变得容易得多。但是我仍然关注函数何时使用std :: future的右值引用作为参数。

更新

通常,右值引用绑定到临时对象。而且非const方法可以由被右引用指向的对象调用吗?我担心这是不合法的,因为非const方法可能会修改临时对象。

1个回答

1

是的,这是合法的,但我认为在factorial中按值接受会更容易。

这是安全的,因为与std :: thread一样,您实际上并没有将引用传回main :: fut_num。因此,main可以退出,而没有悬挂引用。

相反,std :: asyncstd :: thread)从std :: move(fut_num)(有效地存储在新线程的范围内的某个安全位置)move-constructs其自己的std :: future tmp 变量。之后,它调用factorial(std :: move(tmp)

正如我所说,我并不认为这有多大价值,它只节省了一个额外的移动操作,并且您证明代码并不更易读。任何移动操作都将远远不及启动线程(甚至从线程池获取作业)的开销。

请注意,传递lvalue时使用std :: ref是危险的,或者它会复制构造可能是浪费的tmp


"std::async(std::thread) 会通过 std::move(fut_num) 进行移动构造,生成自己的 std::future tmp 变量(安全地存储在新线程的作用域内)。这不应该通过完美转发来实现吗?" - John
你实际上没有将一个引用传回到main::fut_num。因此,如果main退出,就不会有悬空引用了。抱歉,我没听懂。您能用简单的话解释一下吗? - John
@John 当然,有完美转发 :) 我是在谈论你的特定情况... 传递给 std::async(或者说 std::thread)的任何参数都会被完美转发到一些局部变量中,这些变量属于新线程。因此对于 factorial(int&& ref),该参数指向此局部存储而不是 main::fut_num 变量,即使您像 int x=5; std::async(factorial, x) 这样调用它。因此,如果 x 超出范围,异步调用也不受影响。请参见例如 https://dev59.com/_lsX5IYBdhLWcg3wPdQ7#34078246 - Quimby
如果我理解正确,1。为什么“即使x超出作用域,async调用也不受影响”是因为x是按值传递的。如果它被传递为std::ref(x),“如果x超出作用域,async调用将出错。我对吗? 2,这个代码片段是合法的(注释指示修改)。我对吗? - John
  1. 是的,x 被完美转发到 x_temp,并且使用 x_temp 调用了阶乘函数,而不是 x。因此,如果 factorial 接受一个引用,那么它将是对安全的 x_temp 的引用,该引用始终会在线程之外存在,而 x 可能会超出其作用域。是的,确切地说,std::ref 是危险的,因为阶乘函数将接收到对 x 的引用。
  2. 是的,如果你使用了 std::ref,那么你可能会遇到未定义行为,很可能会导致段错误。
- Quimby
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接