FrozenHeart
FrozenHeart

Reputation: 20736

Why can't I use reference in std::future parameters

Why does the following code (on Ideone) give me an error?

#include <future>
#include <iostream>
#include <string>

int main()
{
    int foo = 0;
    bool bar = false;
    std::future<std::string> async_request = std::async(
        std::launch::async,
        [=, &foo](bool& is_pumping_request) -> std::string {
            return "str";
        },
        bar
    );
    std::cout << async_request.get() << std::endl;
}

Output:

In file included from /usr/include/c++/5/future:38:0,
                 from prog.cpp:1:
/usr/include/c++/5/functional: In instantiation of 'struct std::_Bind_simple<main()::<lambda(bool&)>(bool)>':
/usr/include/c++/5/future:1709:67:   required from 'std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...) [with _Fn = main()::<lambda(bool&)>; _Args = {bool&}; typename std::result_of<_Functor(_ArgTypes ...)>::type = std::basic_string<char>]'
prog.cpp:15:2:   required from here
/usr/include/c++/5/functional:1505:61: error: no type named 'type' in 'class std::result_of<main()::<lambda(bool&)>(bool)>'
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/include/c++/5/functional:1526:9: error: no type named 'type' in 'class std::result_of<main()::<lambda(bool&)>(bool)>'
         _M_invoke(_Index_tuple<_Indices...>)
         ^

However, if I change bool& to bool in the parameters list, it compiles successfully.

Why?

Upvotes: 7

Views: 2355

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275270

Consider what would happen if it did bind bar by reference.

Then every time you called std::async, every value you passed would have to last until the async completed.

That would be a recipe for accidental memory corruption. So, std::async instead by default copies everything you pass to it.

It then runs the task on the copy of your input.

Being smart, it tells the code you are calling that the value is non-persistent by moving it into the code. And lvalue references cannot bind to moved-from values.

You can override this behavior by using std::reference_wrapper. async understands reference_wrapper, and it automatically stores a reference to those values and passes them by-reference to the called code.

The easy way to create a reference_wrapper is to call std::ref.

int foo = 0;
bool bar = false;
std::future<std::string> async_request = std::async(
    std::launch::async,
    [=, &foo](bool& is_pumping_request) -> std::string {
        return "str";
    },
    std::ref(bar)
);
std::cout << async_request.get() << std::endl;

and it just works.

This "only pass by reference explicitly" is a safety feature of a binding like operation; because the bound execution can persist beyond the current state, it requires callers only bind by reference explicitly, thus reducing the chance of unintended dangling references.

Upvotes: 8

NathanOliver
NathanOliver

Reputation: 180414

Like std::thread, std::asyc passes the parameters by value to the "function". If you have a function that takes a reference you need to wrap the variable you are passing to asyc with std::ref like

#include <future>
#include <iostream>
#include <string>

int main()
{
    int foo = 0;
    bool bar = false;
    std::future<std::string> async_request = std::async(
        std::launch::async,
        [=, &foo](bool& is_pumping_request) -> std::string {
            return "str";
        },
        std::ref(bar)
    );
    std::cout << async_request.get() << std::endl;
}

Live Example

If the function takes a const & then you need to use std::cref.

Upvotes: 13

Related Questions