匿名函数

匿名函数更通用的称呼是 Lambda 表达式,C++ 的 Lambda 表达式语法如下:

[/* 捕获列表 */](/* 函数参数 */){
    // 函数体
}

Lambda 表达式是一个 右值,不能被取地址。由于没有名字,一般也无法进行递归。

使用 Y 组合子可以进行递归。Y 组合子是 Lambda 演算的一部分。

捕获列表是以逗号分隔的变量列表,例如:

[a, &b](){} (1)
[&](){} (2)
[=](){} (3)
[this](){} (4)
1使用拷贝的形式捕获 a,使用引用的方式拷贝 b
2使用引用捕获当前作用域的所有变量
3使用拷贝捕获当前作用域内的所有变量
4捕获 this。Lambda 中可以直接使用成员变量而无需 this→

当捕获列表为空时,Lambda 表达式的行为与普通的函数指针别无二致,可以当作函数指针类型的形参。一旦捕获列表不为空,Lambda 就变为了一个 闭包 ,行为更接近于重载了 operator() 的对象。但无论是哪一种,都可以使用 std::functional 访问。

无捕获列表的 Lambda 表达式暂且不提,这种形式比较简单,没什么值得注意的,下面谈论闭包。

闭包需要注意的地方有两点:

  • 捕获的引用生命周期不能比闭包结束的晚

  • 捕获的共享指针的生命周期会和闭包一样长

第一种情况最容易出错在 捕获了 this 的闭包 使用了移动语义。例如:

class A {
public:
    A(): func_([this](){ (1)
        // do somthing
    }){}
    A(A&& b){
        func_ = std::move(b.func_); (2)
        // ...
    }
private:
    std::function<void(void)> func_;
};
1代码首先在此处捕获了 this
2代码在此处将 b 的闭包传递给自己。但是此闭包中捕获的 this 指向的是 b,因而导致闭包的生命周期晚于 this 的生命周期。

第二种情况一般被用作内存管理:将智能指针传递给闭包,从而让闭包自动释放内存:

class Session: public std::enable_shared_this<Session>{
public:
    void read(){
        if(!socket_.is_open()) return; (1)

        auto self = enable_shared_this();
        socket_.async_read(asio::buffer(), [self, this](size_t n){ (2)
            if(n<=0) socket_.close();
            read();
        })
    }

private:
    asio::socket socket_;
};
// ...
(new Session)->read(); (3)
1当套接字关闭时退出
2将 self 捕获到闭包中
3创建对象后立即抛弃它,不在持有它的左值

在上面的代码中,创建一个 Session 并调用 self 后就不需要管了,因为 read 创建了一个调用,且每次循环调用都会将自身的智能指针捕获到闭包中,从而防止被析构。当套接字关闭时,闭包结束,self 作为最后一个持有 this 的智能指针也被析构,从而自动完成了内存管理。

Last moify: 2023-12-15 04:28:35
Build time:2025-07-18 09:41:42
Powered By asphinx