当我调用boost::asio::ip::tcp::resolver::async_resolve
时,我的处理程序会收到一个ip::tcp::resolver::iterator
,它会循环遍历一个或多个ip::tcp::resolver::entries
。 他们的一生是什么?如何使他们存活?
例如,如果我得到第一个entry
并向其启动tcp::async_connect
,则可以在async_connect
处理程序中,迭代到下一个entry
并向下一个条目启动另一个async_connect
(只要我将iterator
传递给async_connect
处理程序,当然)?
什么时候清理resolver::iterator
和resolver::entries
?我是否需要做一些特别的事情,还是只是让它们超出范围而不受任何回调闭包的约束?
(我知道我可以在resolver::entries
处理程序中遍历所有async_resolve
并将它们存储在智能指向的结构或其他任何东西中,以便我控制它们的生命周期,但是如果asio::ip::tcp::resolver
已经在处理它,那么我的代码将更加简单让它做好自己的工作。)
最佳答案
解构问题
迭代器具有值语义。因此,它们的生命周期始终与周围对象的生命周期或它们的存储期限(自动堆放,动态堆放,甚至静态堆放)绑定(bind)在一起。
我想您想知道迭代器的有效性。
好吧,the documentation shows,迭代器类别是forward iterator。前向迭代器具有“多次通过保证”,可以重复取消迭代器拷贝的引用,从而获得相同的结果¹。
因此,我们处于半途而废:保留迭代器仍然可以。但是,当然,与任何其他[forward]迭代器一样,我们必须考虑iterator invalidation。
因此,真正的问题归结为:解析程序迭代器使无效时的。
我们知道什么?resolve
函数实现的用例是连接。对于连接而言,第一个有效的端点就足够了,因此不需要它实际保留一个列表。
本着“按需购买”的 spirit ,解析器将状态保持在比所需时间更长的时间是没有意义的。另一方面,如果不支持Multipass,则将迭代器归为ForwardIterator类别是没有意义的。
文档什么也没说。我们只有一种方法:深入代码
潜入守则
我们发现表面以下一些步骤:asio/detail/resolver_service.hpp:73 // Asynchronously resolve a query to a list of entries.
template <typename Handler>
void async_resolve(implementation_type& impl,
const query_type& query, Handler& handler)
{
// Allocate and construct an operation to wrap the handler.
typedef resolve_op<Protocol, Handler> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
boost_asio_handler_alloc_helpers::allocate(
sizeof(op), handler), 0 };
resolve_op
显示迭代器是使用basic_resolver_iterator.hpp::create创建的
这就引出了答案:line 251 typedef std::vector<basic_resolver_entry<InternetProtocol> > values_type;
boost::asio::detail::shared_ptr<values_type> values_;
std::size_t index_;
因此,只要您保留有效迭代器(而不是最终迭代器)的拷贝,就可以继续取消引用它。它甚至会与每个解析程序条目一起保留查询参数(host_name
和service_name
)的拷贝。这似乎有点浪费,但是我想在设计缓存方案时会派上用场。
总结:
译为“它们始终保持有效”(如果它们一直有效)。
¹与例如输入迭代器
²通常,C++实现遵循
零开销原则:不用的东西,不用付钱[C++的设计和演进,1994年]