Reputation: 20736
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
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
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;
}
If the function takes a const &
then you need to use std::cref
.
Upvotes: 13