Asio 的 Handler 类型模板参数推导


#1

asio 要保存 Handler.

template<class Handler>
async_*( xxx , Handler && handler);

在这里, 如果 以 async_read( socket, buffer , boost::bind(xxx) ); 这样的形式调用, 无疑 是一个 右值.

那么, Handlar 的类型就是 bind 返回的类型, 是个完整类型. 这个时候, 如果 asio 需要拷贝 handler 到内置的列队, 就可以很容易的依据 Handler 类型作出.

如果 boost::function func = bind XXX ; async_read( socket, buffer , func ); 这样的形式调用, 无疑 是一个 左值.

那么 Handler 的类型, 其实是一个 boost::function & 类型, 是个引用. 如果 asio 直接拷贝 Handler , 那它拷贝的就是一个引用. 如果 func 对象撤销, 会导致 asio 回调的时候产生严重的问题! 调用到了撤销的对象!!!

于是 asio 这个时候必须将 Handler 的非引用类型求出. 以便正确拷贝对象.

asio 自己编写的代码中, 使用了元编程技术来获取 Handler 的非引用类型. 如果我们自己写 库, 取一个引用, 大大增加了麻烦啊. 特别是, 如果你打算使用 BOOST_ASIO_HANDLE_TYPE (依据是否支持 c++11 展开为 Handler&& handler 或者 Handler & handler) 宏的话, 就必须做 std::remove_reference … 可是这个库在 c++98里是不存在的 … 囧, 这样你就得自己实现一套 c++11 和 c++98 自适应的的一套 remove_reference. asio 内部的宏看来经常变更, 不能直接使用.

所以, 最简单的办法, 就是, 直接使用

template<class Handler>
async_*( xxx , Handler handler);

作为自己的函数的接口. 除非你打算深入元编程, 并打算对 c++98 和 c++11 提供双重机制…

另外,

template<class Handler>
async_*( xxx , Handler handler);

在 C++11 模式下, 编译器虽然生成了临时对象, handler, 但是, 这个 handler 是以 移动构造建立的, 所以相当的廉价.

只有当移动构造的开销都不能承受, 非需要引用的时候, 才去学 asio 那套复杂的元编程机制吧 .


#2

最佳选择Handler &&,其次const reference,因为这个handler是需要保存的