调用提升解析时捕获的值错误::async_resolve



我无法理解下面代码的行为。 当定义符号BUG时,变量this的第三次打印是错误的。

我认为方法resolver::async_resolve中有一些东西会破坏代码。我想了解什么:-)

谢谢

#include <boost/asio.hpp>
#include <iostream>
using namespace std;
template <typename F>
#ifdef BUG
void Connect( boost::asio::ip::tcp::resolver& resolver, F Connected )
#else
void Connect( boost::asio::ip::tcp::resolver& resolver, const F& Connected )
#endif
{
resolver.async_resolve(
boost::asio::ip::tcp::resolver::query{ "localhost", "8088" },
[&Connected]( const boost::system::error_code& ec, boost::asio::ip::tcp::resolver::iterator i )
{
Connected();
}
);
}
struct Test
{
void Start()
{
cout << "this1 " << hex << this << dec << endl;
auto handler = [this]()
{
cout << "this2 " << hex << this << dec << endl;
boost::asio::ip::tcp::resolver resolver{ ios };
Connect( resolver, [this]()
{
cout << "this3 " << hex << this << dec << std::endl;
}
);
};
handler();
ios.run();
}
boost::asio::io_service ios;
};
int main()
{
Test t;
t.Start();
}

您的错误不是由于按值传递到Connect而不是通过常量引用传递,而是由于调用对 lambda 的悬空引用而导致的未定义行为。

这是因为您通过传递给async_resolve的 lambda 中的引用捕获Connnected

resolver.async_resolve(
boost::asio::ip::tcp::resolver::query{ "localhost", "8088" },
[&Connected]( const boost::system::error_code& ec, boost::asio::ip::tcp::resolver::iterator i )
{
Connected(); // Connected is captured by reference
}
);

当调用Connected()时,它已被从堆栈中弹出并销毁。

void Start()
{
cout << "this1 " << hex << this << dec << endl;
auto handler = [this]()
{
cout << "this2 " << hex << this << dec << endl;
boost::asio::ip::tcp::resolver resolver{ ios };
Connect( resolver, [this]()
{
cout << "this3 " << hex << this << dec << std::endl;
}
);
};
handler(); // after this function returns Connected will be destructed
ios.run(); // the thread is blocked in ios.run until the resolve returns
}
  • handler()的调用在堆栈上创建"Connected"lambda并将其传递给Connect,而又创建一个通过引用捕获Connected的lambda,并启动异步操作。

  • 然后handler()返回,从堆栈中弹出"Connected",破坏它。

  • ios.run()防止Test::Start()在等待async_resolve返回时返回。

  • async_resolve完成,并调用其lambda,作为回报,lambda调用Connected(),已被销毁。

您可以通过按值捕获Connected来解决此问题

void Connect( boost::asio::ip::tcp::resolver& resolver, F Connected )
{
resolver.async_resolve(
boost::asio::ip::tcp::resolver::query{ "localhost", "8088" },
[Connected]( const boost::system::error_code& ec, boost::asio::ip::tcp::resolver::iterator i )
{
Connected();
}
);
}

最新更新