Почему этот boost::asio::tcp::socket можно использовать повторно?

Ниже приведен код из примера boost::asio. Почему это нормально, чтобы переместить socket_ член при построении chat_session если рекурсивный вызов в нижней части обработчика собирается передать это же tcp::socket в следующий раз, когда произойдет согласие? Я думал, что после операции перемещения объект больше не безопасен для использования.

class chat_server
{
public:
  chat_server(boost::asio::io_service& io_service,
      const tcp::endpoint& endpoint)
    : acceptor_(io_service, endpoint),
      socket_(io_service)
  {
    do_accept();
  }

private:
  void do_accept()
  {
    acceptor_.async_accept(socket_,
        [this](boost::system::error_code ec)
        {
          if (!ec)
          {
            std::make_shared<chat_session>(std::move(socket_), room_)->start();
          }

          do_accept();
        });
  }

  tcp::acceptor acceptor_;
  tcp::socket socket_;
  chat_room room_;
};

3 ответа

Решение

Код эквивалентен выполнению следующего:

some_class o;
while ( true )
{
  // assign a new instance of some_class to the o variable, calling o.bar() is valid
  o = some_class(...);
  foo(std::move(o));
  // o is no longer valid calling o.bar() would fail
}

Призыв к async_accept повторно инициализирует сокет обратно к допустимому значению, которое можно использовать. Перемещенный объект находится в неопределенном (но допустимом) состоянии, это зависит от разработчика этого объекта. В случае asio::tcp::socket состояние - это неинициализированный сокет, который можно использовать для новых соединений.

Стандарт гласит, что "перемещенный" объект должен быть, по крайней мере, в допустимом неопределенном состоянии.

Перемещенный сокет безопасен в использовании, поскольку его состояние явно указано в документации:

После перемещения перемещенный объект находится в том же состоянии, как если бы он был создан с использованием конструктора basic_stream_socket(io_context&).

И вы правы, объект сокета не может быть использован после перемещения.

Но код, вызывающий вашу лямбду, создаст новый сокет и инициализирует вашу переменную socket_ с этим новым сокетом. Поэтому в следующий раз, когда ваша лямбда будет вызвана, это будет другой сокет.

Другие вопросы по тегам