Is it legal to use an rvalue reference of `std::future` as a parameter?

Viewed 53

Is it legal to use an rvalue reference of std::future as a parameter? Is there any potential problem that I should be aware of, since the std::future::get() is not marked as const.

If I miss something, please let me know.

Here is the full code snippet:

#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;
    }
}

I know if I pass a lvalue reference, things would be much easier. But I still conscious about when the function use a rvalue reference of std::future as a parameter.

UPDATED:

In general, rvalue reference is bound to temporary object. And non-const method could be invoked by the object which is pointed by right references? I am afraid it's illegal because non-const method may modify the temporary object.

1 Answers

Yes, it is legal, but I think accepting by value in factorial would be much easier.

This is safe because, as with std::thread, you are not really passing a reference back to main::fut_num. So main could exit and there would be no dangling references.

Instead, std::async(std::thread) move-constructs its own std::future tmp variable from std::move(fut_num) ( stored somewhere safe, effectively in the scope of the new thread). After that, it calls factorial(std::move(tmp).

As I said, I do not see much value from this, it saves one extra move and you are the proof the code is not more readable. Any moves will pale in comparison to the overhead of launching a thread (or even getting a job from thread pool).

Note that passing lvalue is either dangerous when you use std::ref or it copy constructs tmp which might be wasteful.

Related