I am using asio standalone 1.10.6 and vs2015 rc.
The vs2015 support unique_ptr capture. So I wrote some code looks like:
auto data = std::make_unique<std::string>("abc");
auto buffer = asio::buffer(data->c_str(), data->size());
asio::async_write(s, buffer, [data = std::move(data)](
const asio::error_code& error, size_t byte_transferred) mutable {
do_something(std::move(data), error, byte_transferred);
});
But when I compile the code, the compiler said:
error C2280: .... attempting to reference a deleted function
As my understand, it said that I try to copy the lambda, and because the lambda capture a std::unique_ptr
, so it is noncopyable.
What make me confused is why the asio want to copy the lambda but not move the lambda.
What's wrong with my code? How to workaround it?
=========================
The full code is:
void do_something(std::unique_ptr<std::string> data) { }
void compile_failed() {
asio::io_service io_service;
asio::ip::tcp::socket s(io_service);
auto data = std::make_unique<std::string>("abc");
auto buffer = asio::buffer(data->c_str(), data->size());
asio::async_write(s, buffer, [data = std::move(data)](const asio::error_code& error,
size_t byte_transferred) mutable {
do_something(std::move(data));
});
}
template<typename T > struct lambda_evil_wrap {
mutable T ptr_;
lambda_evil_wrap(T&& ptr) : ptr_(std::forward< T>(ptr)) {}
lambda_evil_wrap(lambda_evil_wrap const& other) : ptr_(std::move(other.ptr_)) {}
lambda_evil_wrap & operator=(lambda_evil_wrap& other) = delete;
};
void compile_success_but_very_danger() {
asio::io_service io_service;
asio::ip::tcp::socket s(io_service);
auto data = std::make_unique<std::string>("abc");
auto buffer = asio::buffer(data->c_str(), data->size());
lambda_evil_wrap<std::unique_ptr<std::string>> wrapper(std::move(data));
asio::async_write(s, buffer, [wrapper](const asio::error_code& error,
size_t byte_transferred) mutable {
do_something(std::move(wrapper.ptr_));
});
}
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
As the code, if I wrapped the unique_ptr to a copyable object, compile is OK. But the lambda_evil_wrap::lambda_evil_wrap(lambda_evil_wrap const& a) is really suck and unsafe. I do not know if asio author wrote some code looks like:
Handler handler2(handler);
handler(...); // Crash here
The error in the original code is that the handler fails to meet the Handler type requirement as it is not CopyConstructible
:
A handler must meet the requirements of
CopyConstructible
types (C++ Std, 20.1.3).
As noted in Boost.Asio's C++11 support for movable handlers, where possible, Boost.Asio will prefer a move constructor over a copy constructor, but the handler must still be copy constructible:
[...] Boost.Asio's implementation will use a handler's move constructor in preference to its copy constructor. In certain circumstances, Boost.Asio may be able to eliminate all calls to a handler's copy constructor. However, handler types are still required to be copy constructible.
To resolve the issue, one may consider using std::shared_ptr
instead of std::unique_ptr
, causing the lambda to be copy constructible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With