【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 的关键。

相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript