Ale*_*kiy 11 c++ lambda perfect-forwarding c++11 stdasync
我有一个函数模板,我想完美转发到我在另一个线程上运行的lambda.这是一个可以直接编译的最小测试用例:
#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;
}
Run Code Online (Sandbox Code Playgroud)
对于非编译情况,这是错误消息的最后一行,它是可理解的:
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> >&’
Run Code Online (Sandbox Code Playgroud)
我觉得它可能与如何T&&推断有关,但我无法确定故障的确切点并修复它.有什么建议?
谢谢!
编辑:我正在使用gcc 4.7.0,以防这可能是一个编译器问题(可能不是)
我理解它的方式你不能使用函数async,因为它期望非const左值引用作为参数,因为它async总是在内部复制它们(或在内部移动它们)以确保它们存在并且在整个线程运行时有效创建.
具体来说,标准说async(launch policy, F&& f, Args&&... args):
(§30.6.8)
(2)需要:
F和每个Ti在Args应满足MoveConstructible要求.INVOKE(DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)(20.8.2,30.3.1.2)应为有效表达.(3)效果:[...]如果policy&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))...)传播的任何异常都作为异常结果存储在共享状态中.
线程对象存储在共享状态中,并影响引用该状态的任何异步返回对象的行为.
不幸的是,这意味着你甚至不能用a替换引用std::reference_wrapper,因为后者不是可移动构造的.我想使用a std::unique_ptr而不是引用会起作用(然而,暗示你的函数参数将始终存在于堆上).
(编辑/更正)
当我意识到std::reference_wrapper实际上实现了一种解决方法时,我正在研究一个相关的问题,尽管我声称相反的情况.
如果你定义了一个在a中包含左值引用std::reference_wrapper但是保持rvalue引用不变的T&&函数,你可以在将它交给之前通过这个函数传递参数std::async.我在wrap_lval下面调用了这个特殊的包装函数:
#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;
}
Run Code Online (Sandbox Code Playgroud)
通过此更改,两个调用都可以accessValueAsync编译和工作.第一个使用左值引用,自动将其包装在一个std::reference_wrapper.当std::async调用lambda函数时,后者会自动转换回左值引用.