I have a function template, where I want to do perfect forwarding into a lambda that I run on another thread. Here is a minimal test case which you can directly compile:
#include <thread>
#include <future>
#include <utility>
#include <iostream>
#include <vector>
/**
* Function template that does perfect forwarding to a lambda inside an
* async call (or at least tries to). I want both instantiations of the
* function to work (one for lvalue references T&, and rvalue reference T&&).
* However, I cannot get the code to compile when calling it with an lvalue.
* See main() below.
*/
template <typename T>
std::string accessValueAsync(T&& obj)
{
std::future<std::string> fut =
std::async(std::launch::async,
[](T&& vec) mutable
{
return vec[0];
},
std::forward<T>(obj));
return fut.get();
}
int main(int argc, char const *argv[])
{
std::vector<std::string> lvalue{"Testing"};
// calling with what I assume is an lvalue reference does NOT compile
std::cout << accessValueAsync(lvalue) << std::endl;
// calling with rvalue reference compiles
std::cout << accessValueAsync(std::move(lvalue)) << std::endl;
// I want both to compile.
return 0;
}
For the non-compiling case, here is the last line of the error message which is intelligible:
main.cpp|13 col 29| note: no known conversion for argument 1 from ‘std::vector<std::basic_string<char> >’ to ‘std::vector<std::basic_string<char> >&’
I have a feeling it may have something to do with how T&& is deduced, but I can’t pinpoint the exact point of failure and fix it. Any suggestions?
Thank you!
EDIT: I am using gcc 4.7.0 just in case this could be a compiler issue (probably not)
The way I understand it you cannot use a function through
asyncthat expects non-const lvalue references as arguments, becauseasyncwill always make a copy of them internally (or move them inside) to ensure they exist and are valid throughout the running time of the thread created.Specifically, the Standard says about
async(launch policy, F&& f, Args&&... args):Unfortunately, this means you cannot even replace the reference with a
std::reference_wrapper, because the latter isn’t move-constructible. I suppose using astd::unique_ptrinstead of the reference would work (implying, however, that your function arguments will always live on the heap).(EDIT/CORRECTION)
I was working on a related problem when I realized that
std::reference_wrapperactually enables a workaround, although I claimed the opposite above.If you define a function that wraps lvalue references in a
std::reference_wrapper, but leaves rvalue references unchanged, you can pass theT&&argument through this function before handing it over tostd::async. I have called this special wrapper functionwrap_lvalbelow:With this change, both calls to
accessValueAsynccompile and work. The first one, which uses an lvalue reference, automatically wraps it in astd::reference_wrapper. The latter is automatically converted back to an lvalue reference whenstd::asynccalls the lambda function.