调用boost resolve :: async_resolve时捕获值的值错误

时间:2016-11-24 17:02:56

标签: c++11 lambda boost-asio

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

1 个答案:

答案 0 :(得分:1)

您的错误不是由于值传递给Connect而是由const引用传递给它,因为调用了对lambda的悬空引用,它的未定义行为。

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

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()会创建&#34; Connected&#34;堆栈上的lambda并将其传递给Connect,然后创建一个lambda ,它通过引用捕获Connected,并启动异步操作。

  • handler()然后返回,弹出&#34; Connected&#34;离开堆栈,破坏它。

  • 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();
        }
    );
}