#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; }
对于非编译的情况,这里是可理解的错误消息的最后一行:
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> >&’
我有一种感觉,它可能与T&&被推断,但我无法确定确切的故障点并修复它.有什么建议么?
谢谢!
编辑:我正在使用gcc 4.7.0,以防这可能是一个编译器问题(可能不是)
解决方法
具体来说,该标准介绍了异步(发射政策,F&& F,Args& … args):
(§30.6.8)
(2) Requires:
F
and eachTi
inArgs
shall satisfy the MoveConstructible requirements.INVOKE(DECAY_COPY (std::forward<F>(f)),DECAY_COPY (std::forward<Args>(args))...)
(20.8.2,30.3.1.2) shall be a valid expression.(3) Effects: […] if policy & launch::async is non-zero — calls
INVOKE(DECAY_COPY (std::forward<F>(f)),30.3.1.2) as if in a new thread of execution represented by a thread object with the calls to
DECAY_COPY()
being evaluated in the thread that called async. Any return value is stored as the result in the shared state. Any exception propagated from the execution of INVOKE (DECAY_COPY (std::forward(f)),DECAY_COPY (std::forward(args))…) is stored as the exceptional result in the shared state.
The thread object is stored in the shared state and affects the behavior of any asynchronous
return objects that reference that state.
不幸的是,这意味着你甚至不能用std :: reference_wrapper替换引用,因为后者不是可移动的.我想使用std :: unique_ptr而不是引用将工作(这意味着,但是,你的函数参数将始终存在于堆上).
(EDIT /校正)
当我意识到std :: reference_wrapper实际上可以解决这个问题时,我正在处理一个相关的问题,尽管我声称相反.
如果你定义一个函数,它将lvalue引用包含在std :: reference_wrapper中,但是使rvalue引用不变,你可以通过T&&在通过此函数之前将其转换为std :: async.我已经在这里调用了这个特殊的wrapper函数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; }
有了这个变化,两个调用accessValueAsync都可以编译并工作.第一个使用了一个左值引用,自动将其包装在一个std :: reference_wrapper中.当std :: async调用lambda函数时,后者被自动转换为一个左值引用.