【QT】 Lambda 表达式

一、基础语法与概念
1.什么是 Lambda 表达式?

一种匿名函数对象,可以在代码中就地定义,无需专门的函数或函数对象类。

核心优势:简洁、闭包(能够捕获上下文变量)。

2.完整语法(C++11及以后)

bash 复制代码
[ capture-list ] ( parameters ) -> return-type { body }

[ capture-list ](捕获列表)这是lambda表达式最核心,最独特的部分,定义了哪些外部变量能被lambda内部使用,以及捕获方式(值捕获vs引用捕获),捕获列表可以为空,表示不访问任何外部变量,也可以使用默认捕获方式来表示按引用或者按值捕获所有外部变量,还可以混合使用具体的变量名和默认捕获方式混合使用。

( parameters )(参数列表)和普通函数的参数列表一样,可以为空,表示没有参数,C++14中使用auto来实现泛型参数。

-> return-type(返回值类型):可选的,如果省略,编译器会根据函数体中的return推导,C++14中使用auto来实现泛型返回值。

{ body }(函数体):Lambda要执行的代码, c++14 中使用 constexpr 来实现编译期计算。

cpp 复制代码
最简单的Lambda:不捕获参数,也不接受参数
void test()
{
	auto hello = []{std::cout<<"Hello,Lambda!"<<std::endl;}
	hello();//调用
}

二、捕获列表

1.捕获的方式
值捕获[=]:捕获所有外部变量的副本。lambda内部修改不影响外部变量

cpp 复制代码
    int x = 10,y = 20;
    auto valueCapture = [x,y]{
        std::cout <<"x= "<<x<<",y= "<<y<<std::endl;
    };
    valueCapture();
    x = 200;
    y = 300;
    valueCapture();//外部值改变不影响lambda里面捕获的值

引用捕获[&]:捕获所有外部变量的引用。lambda内部修改会影响外部变量

cpp 复制代码
  引用捕获:内部变量 外部变量会相互影响
    int x = 10,y = 20;
    auto valueCapture = [&x,&y]{
        x++;
        y++;
        std::cout <<"lamda x= "<<x<<",y= "<<y<<std::endl;
    };
    valueCapture();//x= 11,y= 21
    std::cout <<"x= "<<x<<",y= "<<y<<std::endl;//x= 11,y= 21
    x = 200;
    y = 300;
    valueCapture();//x= 201,y= 301
    std::cout <<"222 x= "<<x<<",y= "<<y<<std::endl;//x= 201,y= 301

混合捕获与显式捕获
[x,&y] :值捕获x,引用捕获y。
[=,&y] :默认值捕获所有变量,但y显示指定为引用捕获。
[&,x]:默认引用捕获所有值,但x显式指定为值捕获

cpp 复制代码
    int a=1, b=2, c=3;
    //默认值捕获,但显式指定某些变量引用捕获
    auto mixed = [=,&a,&b]()mutable{
        //a,b是引用捕获,可以修改
        a = 10;
        b = 20;
        //c是值捕获,不能修改,除非()后加上mutable关键字
        c = 30;
        std::cout <<"lambda a="<<a<<",b= "<<b<<",c="<<c<<std::endl;
    };
    //默认引用捕获,但显式指定某些变量值捕获
    auto mixed2 = [&,a]()mutable{
        //a是值捕获不能修改,除非()后加上mutable关键字
        a = 100;
        //b,c是引用捕获,可以修改
        b = 200;
        c = 300;
        std::cout <<"lambda2 a="<<a<<",b= "<<b<<",c="<<c<<std::endl;
    };
    mixed();//lambda a=10,b= 20,c=30
	  //a=10,b= 20,c=3
    std::cout <<"a="<<a<<",b= "<<b<<",c="<<c<<std::endl;
    
    mixed2();//lambda2 a=100,b= 200,c=300
    //222 a=10,b= 200,c=300
    std::cout <<"222 a="<<a<<",b= "<<b<<",c="<<c<<std::endl;

2.mutable 关键字

默认情况下,值捕获的变量在lambda体内是const的,不能被修改。

使用mutable 可以移除这个const限制,允许修改值捕获的副本。
3.捕获 this 指针

在类的成员函数中,Lambda 可以通过 [this] 或 [=] 捕获当前对象的 this 指针,从而访问类的成员变量和函数。

三、返回值类型和模板
1.返回值类型推导

绝大多数情况下,可以省略 -> return-type,编译器会自动推导,当函数体中有多个 return 语句且类型不完全相同,或者逻辑复杂编译器无法推导时,需要显式指定。

2.lambda与泛型(C++14)

可以使用auto作为参数类型,创造泛型Lambda

cpp 复制代码
auto add = [](auto a,auto b){return a+b;};
std::cout<<add(1,2)<<std::endl; //int 
std::cout<<add(1.1,2.2)<<std::endl;//double

四、高级特性与应用
1.lambda的本质

lambda是一个函数对象。编译器会为每个lambda表达式生成一个唯一的,匿名的类类型

这个类重载了operator(),使得对象可以像函数一样被调用。

2.将 Lambda 作为参数传递

lambda最常用的场景是作为算法的谓词,传递给STL算法。

cpp 复制代码
#include <algorithm>
#include <vector>
std::vector<int> vec={5,2,8,1,9};
std::sort(vec.begin(),vec.end(),[](int a,int b)){return a>b;});//降序排序

3.std::function 包装器

lambda的类型是唯一的、匿名的、无法直接声明。

可以使用std::function来存储和传递任何可调用对象(包括lambda)

cpp 复制代码
#include <functional>
std::function<int(int,int)>addr = [](int a,int b){return a+b;};

4.初始化捕获(C++14 广义捕获)

cpp 复制代码
auto ptr = std::make_unique<int>(42);
//将ptr 移动(而并非复制)到Lambda中
auto lambda = [p=std::move(ptr)]{return *p;};

5.constexpr Lambda(C++17)

cpp 复制代码
可以在编译期求值的Lambda,用于元编程和性能优化。
constexpr auto square = [](int n) { return n * n; };
static_assert(square(5) == 25); // 编译期计算
constexpr:表明这个lambda可以在编译期间被求值
编译期执行:square(5)不是在运行时计算的,而是在编译器处理代码时就已经计算出结果25
类型安全:如果计算结果不匹配,编译直接失败
零运行时开销:最终代码中不会包含这个计算过程

6.模板语法(C++20)

cpp 复制代码
Lambda 支持显式的模板参数列表。
[捕获列表] <模板参数列表> (函数参数列表) -> 返回类型 { 函数体 }
auto foo = []<typename T>(T t) { /* ... */ };

为什么需要这个特性?
1. 类型约束和保证,如果需要参数的变量都是一样的话,
使用auto genericLambda = [](auto a, auto b) {return a + b;};就没办法保证
auto explicitLambda = []<typename T>(T a, T b) {return a + b;};这两个参数就要一样
2. 访问容器元素类型
//处理容器,需要知道元素类型
auto processContainer = []<typename T>(const std::vector<T>& vec) {
    //可以在函数体内使用 T
    T sum{};
    for (const auto& element : vec) {
        sum += element;
    }
    return sum;
};

1.掌握基础语法和捕获列表。这是最核心的部分,务必理解值捕获、引用捕获和 mutable 的用法。

2.与 STL 算法结合。用 std::sort, std::for_each, std::find_if 等算法来熟悉 Lambda 作为谓词的用法。

3.:理解其本质。明白 Lambda 是一个编译器生成的函数对象,这有助于理解它的行为和限制。

4.:在实战中应用高级特性。当你在项目中遇到需要封装局部状态或传递复杂回调时,自然会用到 std::function、初始化捕获等高级特性。

捕获列表 [ ] 是 C++ Lambda 区别于其他语言 Lambda 的关键。

相关推荐
疯狂的喵2 小时前
C++编译期多态实现
开发语言·c++·算法
2301_765703142 小时前
C++中的协程编程
开发语言·c++·算法
m0_748708052 小时前
实时数据压缩库
开发语言·c++·算法
lly2024062 小时前
jQuery Mobile 表格
开发语言
惊讶的猫3 小时前
探究StringBuilder和StringBuffer的线程安全问题
java·开发语言
m0_748233173 小时前
30秒掌握C++核心精髓
开发语言·c++
Fleshy数模3 小时前
从数据获取到突破限制:Python爬虫进阶实战全攻略
java·开发语言
Duang007_4 小时前
【LeetCodeHot100 超详细Agent启发版本】字母异位词分组 (Group Anagrams)
开发语言·javascript·人工智能·python
froginwe114 小时前
Redis 管道技术
开发语言
u0109272714 小时前
C++中的RAII技术深入
开发语言·c++·算法