无法将 std::p ackaged_task 移动到 lambda 中



我想推迟循环中打包任务的执行。

class ILoop {
public:
virtual void Deffer(std::function<void()>&& task) = 0;
void Deffer(std::function<void()>& task) = delete;
static void set_loop(ILoop*);
static ILoop& loop();
private:
static ILoop* loop_;
};
template <typename T>
struct LoopExecutor {
std::future<T> Commit(std::function<T()>&& task) {
std::packaged_task<T()> wrapper([t = std::move(task)] { return t(); });
std::future<T> future = wrapper.get_future();
ILoop::loop().Deffer([wrapper = std::move(wrapper)] { wrapper(); });
return future;
}
};

当我试图编译这个时

auto t = executor.Commit([] { return 100; });

我有编译错误:

[1/2] Building CXX object unit_tests/CMakeFiles/reduct_tests.dir/reduct/async/awaiters_test.cc.o
FAILED: unit_tests/CMakeFiles/reduct_tests.dir/reduct/async/awaiters_test.cc.o 
/usr/bin/g++-11  -I/home/atimin/.conan/data/fmt/8.0.1/_/_/package/20da98908a15523bbe9f086a5c57a4bde424a9c0/include -I/home/atimin/.conan/data/uwebsockets/20.8.0/_/_/package/c84c7dca9672f88b95bafb2f5754a22669d1bbe5/include -I/home/atimin/.conan/data/uwebsockets/20.8.0/_/_/package/c84c7dca9672f88b95bafb2f5754a22669d1bbe5/include/uWebSockets -I/home/atimin/.conan/data/nlohmann_json/3.9.1/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include -I/home/atimin/.conan/data/gtest/1.11.0/_/_/package/63868df56b76903d4ad40ecbd5b2e8238cee50c9/include -I/home/atimin/.conan/data/catch2/2.13.7/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include -I/home/atimin/.conan/data/zlib/1.2.11/_/_/package/be27726f9885116da1158027505be62e913cd585/include -I/home/atimin/.conan/data/usockets/0.8.1/_/_/package/f3766c5a18b2feee4dc2c3a94335008aaaea2543/include -I/home/atimin/Projects/flipback/reduct-storage/src -I/home/atimin/Projects/flipback/reduct-storage/cmake-build-debug-gcc11 -I/home/atimin/Projects/flipback/reduct-storage/unit_tests -g  -DLIBUS_NO_SSL -std=gnu++20 -MD -MT unit_tests/CMakeFiles/reduct_tests.dir/reduct/async/awaiters_test.cc.o -MF unit_tests/CMakeFiles/reduct_tests.dir/reduct/async/awaiters_test.cc.o.d -o unit_tests/CMakeFiles/reduct_tests.dir/reduct/async/awaiters_test.cc.o -c /home/atimin/Projects/flipback/reduct-storage/unit_tests/reduct/async/awaiters_test.cc
In file included from /home/atimin/Projects/flipback/reduct-storage/src/reduct/async/awaiters.h:10,
from /home/atimin/Projects/flipback/reduct-storage/unit_tests/reduct/async/awaiters_test.cc:3:
/home/atimin/Projects/flipback/reduct-storage/src/reduct/async/executors.h: In instantiation of ‘std::future<_Res> reduct::async::LoopExecutor<T>::Commit(std::function<T()>&&) [with T = int]’:
/home/atimin/Projects/flipback/reduct-storage/src/reduct/async/awaiters.h:40:68:   required from ‘reduct::async::Run<T, Executor>::Run(std::function<T()>&&) [with T = int; Executor = reduct::async::LoopExecutor<int>]’
/home/atimin/Projects/flipback/reduct-storage/unit_tests/reduct/async/awaiters_test.cc:52:92:   required from here
/home/atimin/Projects/flipback/reduct-storage/src/reduct/async/executors.h:19:66: error: no match for call to ‘(const std::packaged_task<int()>) ()’
19 |     ILoop::loop().Deffer([wrapper = std::move(wrapper)] { wrapper(); });
|                                                           ~~~~~~~^~
In file included from /home/atimin/Projects/flipback/reduct-storage/src/reduct/async/awaiters.h:8,
from /home/atimin/Projects/flipback/reduct-storage/unit_tests/reduct/async/awaiters_test.cc:3:
/usr/include/c++/11/future:1577:7: note: candidate: ‘void std::packaged_task<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) [with _Res = int; _ArgTypes = {}]’ (near match)
1577 |       operator()(_ArgTypes... __args)
|       ^~~~~~~~
/usr/include/c++/11/future:1577:7: note:   passing ‘const std::packaged_task<int()>*’ as ‘this’ argument discards qualifiers
In file included from /usr/include/c++/11/future:47,
from /home/atimin/Projects/flipback/reduct-storage/src/reduct/async/awaiters.h:8,
from /home/atimin/Projects/flipback/reduct-storage/unit_tests/reduct/async/awaiters_test.cc:3:
/usr/include/c++/11/bits/std_function.h:414:9: error: ‘std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = reduct::async::LoopExecutor<int>::Commit(std::function<int()>&&)::<lambda()>; <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Res = void; _ArgTypes = {}]’, declared using local type ‘reduct::async::LoopExecutor<int>::Commit(std::function<int()>&&)::<lambda()>’, is used but never defined [-fpermissive]
414 |         function(_Functor __f)

事实上,即使是更简单的代码(没有延迟功能(也不起作用:


auto t = [w = std::move(wrapper)] { w(); };
t();

另一方面,它是可以的:

std::thread t(std::move(wrapper));

我使用GCC 11.2。

默认情况下,lambda的调用运算符是const限定的。

因此,在lambda的内部,指向lambda的this指针也是const限定的,成员wrapper也是。std::packaged_task没有const限定的operator(),因此无法调用它。

您可以通过添加mutable关键字使lambda的operator()不符合const条件:

[wrapper = std::move(wrapper)]()mutable{ wrapper(); }

请注意,当前带有说明符的lambda的语法要求将空参数列表给定为()


但是,您有一个更大的问题:在lambda中存储std::packaged_task会使lambda不可复制。但是std::function要求存储的类型是可复制的。您不能将其用于此用例,另请参阅如何从移动捕获lambda表达式创建std::函数?

最新更新