当我调用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::iteratorresolver::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_nameservice_name)的拷贝。这似乎有点浪费,但是我想在设计缓存方案时会派上用场。

总结:
  • 问:何时解析程序迭代器无效?
  • A. 销毁有效迭代器的最后一个拷贝时。

  • 译为“它们始终保持有效”(如果它们一直有效)。

    ¹与例如输入迭代器

    ²通常,C++实现遵循
    零开销原则:不用的东西,不用付钱[C++的设计和演进,1994年]

    09-06 22:30