据我理解,你不能通过以下方式使用函数async
期望非常量左值引用作为参数,因为async
将始终在内部制作它们的副本(或将它们移动到内部),以确保它们存在并且在创建的线程的整个运行时间内有效。
具体来说,该标准规定了async(launch policy, F&& f, Args&&... args)
:
(§30.6.8)
(2) 要求:F
和每个Ti
in Args
应满足 MoveConstructible 要求。INVOKE(DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)
(20.8.2, 30.3.1.2) 应是有效的表达式。
(3) 效果:[...] 如果策略和 launch::async 不为零 — 调用INVOKE(DECAY_COPY (std::forward<F>(f)),DECAY_COPY (std::forward<Args>(args))...)
(20.8.2, 30.3.1.2) 就好像在一个由线程对象表示的新执行线程中,并调用DECAY_COPY()
在调用 async 的线程中进行评估。任何返回值都作为结果存储在共享状态中。从执行 INVOKE (DECAY_COPY (std::forward(f))、DECAY_COPY (std::forward(args))...) 传播的任何异常都会作为异常结果存储在共享状态中。
线程对象以共享状态存储并影响任何异步的行为
返回引用该状态的对象。
不幸的是,这意味着您甚至无法用std::reference_wrapper
,因为后者不可移动构造。我想使用std::unique_ptr
而不是引用会起作用(但是,这意味着您的函数参数将始终存在于堆上)。
(编辑/更正)
当我意识到时,我正在研究一个相关的问题std::reference_wrapper
实际上可以实现一种解决方法,尽管我在上面声称相反。
如果您定义一个将左值引用包装在std::reference_wrapper
,但保持右值引用不变,您可以传递T&&
在将其传递给之前通过此函数进行参数std::async
。我已经调用了这个特殊的包装函数wrap_lval
below:
#include <thread>
#include <future>
#include <utility>
#include <iostream>
#include <vector>
#include <type_traits>
/* First the two definitions of wrap_lval (one for rvalue references,
the other for lvalue references). */
template <typename T>
constexpr T&&
wrap_lval(typename std::remove_reference<T>::type &&obj) noexcept
{ return static_cast<T&&>(obj); }
template <typename T>
constexpr std::reference_wrapper<typename std::remove_reference<T>::type>
wrap_lval(typename std::remove_reference<T>::type &obj) noexcept
{ return std::ref(obj); }
/* The following is your code, except for one change. */
template <typename T>
std::string accessValueAsync(T&& obj)
{
std::future<std::string> fut =
std::async(std::launch::async,
[](T&& vec) mutable
{
return vec[0];
},
wrap_lval<T>(std::forward<T>(obj))); // <== Passing obj through wrap_lval
return fut.get();
}
int main(int argc, char const *argv[])
{
std::vector<std::string> lvalue{"Testing"};
std::cout << accessValueAsync(lvalue) << std::endl;
std::cout << accessValueAsync(std::move(lvalue)) << std::endl;
return 0;
}
通过此更改,两者都调用accessValueAsync
编译并工作。第一个使用左值引用,自动将其包装在std::reference_wrapper
。后者会在以下情况下自动转换回左值引用:std::async
调用 lambda 函数。