如何编写一个将自身作为回调传递的匿名函数/ lambda?

pyo*_*yon 7 c++ c++11

我正在学习boost :: asio和C++ 11.我的一个测试程序,实际上是对boost :: asio教程中给出一个示例的改编,如下所示:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class printer {

// Static data members
private:
    const static boost::posix_time::seconds one_second;

// Instance data members
private:
    boost::asio::deadline_timer timer;
    int count;

// Public members
public:
    printer(boost::asio::io_service& io)
        : timer(io, one_second), count(0) {

        std::function<void(const boost::system::error_code&)> callback;
        callback = [&](const boost::system::error_code&) { // critical line
            if (count < 5) {
                std::cout << "Current count is " << count++ << std::endl;

                timer.expires_at(timer.expires_at() + one_second);
                timer.async_wait(callback);
            }
        };

        timer.async_wait(callback);
    }

    ~printer() {
        std::cout << "Final count is " << count << std::endl;
    }
};

const boost::posix_time::seconds printer::one_second(1);

int main() {
    boost::asio::io_service io;
    printer p(io);
    io.run();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当我运行这个程序时,我得到一个分段错误.我明白为什么会出现分段错误.构造函数完成运行后,构造函数的callback变量超出范围,lambda的callback变量(对构造函数callback变量的引用)变为悬空引用.

所以我修改了关键线:

        callback = [callback, &](const boost::system::error_code&) { // critical line
Run Code Online (Sandbox Code Playgroud)

然后编译它,运行它,并得到一个错误的函数调用错误.再次,我明白为什么我得到了错误的函数调用错误.在lambda的范围内,构造函数的callback变量仍未被赋值给任何值,因此它实际上是一个悬空函数指针.因此,lambda的callback变量是构造函数callback变量的副本,也是一个悬空函数指针.


在考虑了这个问题一段时间后,我意识到我真正需要的是回调能够使用函数指针引用自身,而不是对函数指针的引用.该示例通过使用命名函数作为回调而不是匿名函数来实现此目的.但是,将命名函数作为回调传递并不是很优雅.有没有办法让一个匿名函数有一个函数指针将自己作为一个局部变量?

Nic*_*las 8

有几种选择:

  • 停止使用lambda.你知道,你不必将它们用于一切.它们涵盖了很多案例,但它们并不涵盖所有内容.只需使用常规的旧仿函数.
  • 让lambda存储一个指向std::function存储lambda 的动态分配的智能指针.例如:

    auto pCallback = std::make_shared<std::function<void(const boost::system::error_code&)>>();
    auto callback = [=](const boost::system::error_code&) { // critical line
        if (count < 5) {
            std::cout << "Current count is " << count++ << std::endl;
    
            timer.expires_at(timer.expires_at() + one_second);
            timer.async_wait(pCallback.get());
        }
    };
    *pCallback = callback;
    
    Run Code Online (Sandbox Code Playgroud)


Ada*_*itz 1

lambda 通过引用捕获局部变量“callback”,当 lambda 运行时,该变量将无效。