我无法理解下面代码的行为。 当定义符号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();
}
);
}