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

Wrong value for captured value when calling boost resolve::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();
}

您的错误不是由于通过值而不是通过 const 引用传递给 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,后者又创建了一个 lambda 通过引用捕获Connected,并开始异步操作。

  • handler() 然后 returns,从堆栈弹出“Connected”,销毁它。

  • ios.run() 在等待 async_resolve 到 return.

    [ 时阻止 Test::Start() returning =53=]
  • async_resolve 完成,并调用其 lambda,后者在 return 中调用 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();
        }
    );
}