I cannot identify any material that explains the reasoning as to why handlers are required to be CopyConstructible
, but it is explicitly noted even in the presence of C++11 support. The Movable Handlers documentation notes:
As an optimisation, user-defined completion handlers may provide move constructors, and 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.
The type checking Asio performs allows for friendlier compiler error messages when a type requirement is not satisfied. This type checking occurs earlier in the call stack, and is not predicated on if the use of the object would generate a compiler error. For example, when type checking is enabled, type checking will emit a compiler error for a handler that does not have a copy constructor, even if all calls to the handler's copy constructor have been eliminated. By defining BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
, one can disable the explicit type checking, and allow for compiler errors to surface from call points deeper in the implementation if they occur. The History notes this option:
Asio 1.6.0 / Boost 1.47
- ...
- Added friendlier compiler errors for when a completion handler does not meet the necessary type requirements. When C++0x is available (currently supported for
g++
4.5 or later, and MSVC 10), static_assert
is also used to generate an informative error message. This checking may be disabled by defining BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
.
Here is a complete example demonstrating this functionality. Within it, ownership of a socket managed by unique_ptr
is transferred to a handler via std::move()
:
#include <functional> // std::bind
#include <memory> // std::unique_ptr
#include <string> // std::string
#define BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
#include <boost/asio.hpp>
const auto noop = std::bind([]{});
int main()
{
using boost::asio::ip::tcp;
// Create all I/O objects.
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
auto socket1 = std::make_unique<tcp::socket>(std::ref(io_service));
tcp::socket socket2(io_service);
// Connect the sockets.
acceptor.async_accept(*socket1, noop);
socket2.async_connect(acceptor.local_endpoint(), noop);
io_service.run();
io_service.reset();
// Move ownership of socket1 to a handler that will write to the
// socket.
const std::string expected_message = "test message";
io_service.post([socket1{std::move(socket1)}, &expected_message] {
boost::asio::write(*socket1, boost::asio::buffer(expected_message));
});
io_service.run();
// Read from socket2.
std::vector<char> actual_message(socket2.available());
boost::asio::read(socket2, boost::asio::buffer(actual_message));
// Verify message.
assert(std::equal(
begin(expected_message), end(expected_message),
begin(actual_message), end(actual_message)));
}