C++之Lambda表达式

参考

Lambda是C++11引入的匿名函数对象,算是一个语法糖,而且还在不断发展。

1. 特点

  • 就地定义,无需单独命名
  • 可捕获上下文变量
  • 可自动推导返回类型(多数情况)
  • 可作为函数参数传递

2. 语法

[capture](parameters) mutable -> return_type { //Body }

  • [capture]:捕获列表,用于指定Lambda如何访问外部变量。

  • (parameters):参数列表,和普通函数的参数类似,但需要注意Lambda参数C++14起才支持auto类型。且不允许有默认参数。

  • mutable:这个关键字的作用是允许修改按值捕获的变量,或者调用非const的成员函数。默认情况下Lambda的operator()是const的,所以不加mutable的话,无法修改按值捕获的变量。

  • return_type:返回类型,通常可以自动推导,但在某些情况下需要显式指定,比如函数体内有多个return语句且返回类型不一致时。

  • 函数体:Lambda的具体实现代码,和普通函数类似,但可以访问捕获的变量。

3. 示例

这是官方给出的一个标准示例,实例中std::sort函数第三个参数应该是传递一个排序规则的函数,但是这个实例中直接将排序函数的实现写在应该传递函数的位置,省去了定义排序函数的过程,对于这种不需要复用,且短小的函数,直接传递函数体可以增加代码的可读性。

#include <algorithm>
#include <cmath>

void abssort(float* x, unsigned n) {
    std::sort(x, x + n,
        // Lambda expression begins
        [](float a, float b) {
            return (std::abs(a) < std::abs(b));
        } // end of lambda expression
    );
}

4. Lambda表达式参数详解

4.1 Lambda捕获列表

Lambda表达式与普通喊数最大的区别在于,除了可以使用参数外,Lambda表达式还可以通过捕获列表访问一些上下文中的数据。具体地,捕获列表描述了上下文那些数据可以被Lambda使用,以及使用方式(值传递或引用传递)。语法上,在[]包括起来的的是捕获列表,捕获列表有多个捕获项组成,并以逗号分隔。捕获列表有以下几种形式:

  • []表示捕获任何变量
auto function = ([]{
		std::cout << hello world! << std::endl;
	}
);

function();
  • [var]表示值传递方捕获变量var,外面只改变并不会改变Lambda捕获时的值
int main(int argc, char** argv) {
	int num = 100;
	auto function = ([num]{
			std::cout << "num1: " << num << std::endl;
		}
	);
	num = 1000;
	function();
	std::cout << "num2: " << num << std::endl;
}

image

  • [=]表示值传递方式捕获所有父作用域的变量,包括this
int main(int argc, char** argv) {
	int num = 100;
	auto function = ([=]{
			std::cout << "num1: " << num << std::endl;
		}
	);
	num = 1000;
	function();
	std::cout << "num2: " << num << std::endl;
}

image

  • [&var]表示引用传递方式捕获变量,跟值传递不同,外部值改变会实时更新,lambda表达式内也能更改捕获的值
int main(int argc, char** argv) {
	int num = 100;
	auto function = ([&num]{
			std::cout << "num1: " << num << std::endl;
			num = 11;
		}
	);
	num = 1000;
	function();
	std::cout << "num2: " << num << std::endl;
}

image

  • [=,&]拷贝与引用混用
int main(int argc, char** argv) {
	int index = 1;
	int num = 100;
	auto function = ([=,&num]{
			num = 1000;
			std::cout << "index1: " << index << "num1: " << num << std::endl;
			
		}
	);
	function();
}

image

4.2 Lambda参数列表

int main(int argc, char** argv) {
	int index = 1;
	int num = 100;
	auto function = [](int first, int second){
			return first + second;		
		};
	auto ret = function(100,200); 	
	std::cout << "ret: " << ret << std::endl;
}

image

4.3 可变规格mutable

默认情况下,Lambda函数总是一个const函数,mutable修饰符可以取消其常亮性。在使用该修饰符时,参数列表不可省略(即使参数为空)。

#include <iostream>
using namespace std;

int main()
{
   int m = 0;
   int n = 0;
   [&, n] (int a) mutable { 
   	m = ++n + a; //不加mutable,提示++n报错 
   }(4);
   cout << m << endl << n << endl;
}

image

4.4 返回类型

Lambda表达式的返回类型会自动推导。除非你指定了返回类型,否则不必使用关键字。返回型类似于通常的方法或函数的返回型部分。但是,返回类型必须在参数列表之后,并且必须在返回类型->之前包含类型关键字。如果lambda主体仅包含一个return语句或该表达式未返回值,则可以省略Lambda表达式的return-type部分。如果lambda主体包含一个return语句,则编译器将从return表达式的类型中推断出return类型。否则,编译器将返回类型推导为void。
auto x1 = [](int i){ return i; };

4.5 Lambda函数体

5. Lambda适用场景

  • 应用于STL算法库
#include <algorithm>
#include <cmath>
using namespace std;
int main()
{
	// for_each应用实例
	int a[4] = {33,4,11,2};
	sort(a, a+4, [=](int x, int y) -> bool { 
		return x%10 < y%10; 
	} );
	for_each(a, a+4, [=](int x) { 
		cout << x << " ";
	} );
}

image

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
int main()
{
	// remove_if应用实例
	std::vector<int> vec_data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
	int x = 5;
	vec_data.erase(std::remove_if(vec_data.begin(), vec_data.end(), [=](int i) { 
	    return i < x;}), vec_data.end());
	
	std::for_each(vec_data.begin(), vec_data.end(), [](int i) { 
	    std::cout << i << std::endl;});
}

image

  • 短小不需要复用函数场景
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main(void)
{
    int data[6] = { 3, 4, 12, 2, 1, 6 };
    vector<int> testdata;
    testdata.insert(testdata.begin(), data, data + 6);

    // 对于比较大小的逻辑,使用lamdba不需要在重新定义一个函数
    sort(testdata.begin(), testdata.end(), [](int a, int b){ 
        return a > b; });
	for	(int a : testdata)
	{
		std::cout << a << std::endl;
	} 
    return 0;
}

image

  • 应用于多线程场景
clude <iostream>
#include <thread>
#include <vector>
#include <algorithm>

int main()
{
    // vector 容器存储线程
    std::vector<std::thread> workers;
    for (int i = 0; i < 5; i++) 
    {
        workers.push_back(std::thread([]() 
        {
            std::cout << "thread function\n";
        }));
    }
    std::cout << "main thread\n";

    // 通过 for_each 循环每一个线程
    // 第三个参数赋值一个task任务
    // 符号'[]'会告诉编译器我们正在用一个匿名函数
    // lambda函数将它的参数作为线程的引用t
    // 然后一个一个的join
   std::for_each(workers.begin(), workers.end(), [](std::thread &t)									 
    {
        t.join();
    });	

    return 0;						
}

image

  • 应用于函数指针与function
#include <iostream>
#include <functional>
using namespace std;

int main(void)
{
    int x = 8, y = 9;
    auto add = [](int a, int b) { return a + b; };
    std::function<int(int, int)> Add = [=](int a, int b) { return a + b; };

    cout << "add: " << add(x, y) << endl;
    cout << "Add: " << Add(x, y) << endl;

    return 0;
}

image

  • 作为函数的入参
void DataCallback(FuncCallback callback)
{
	std::cout << "Start FuncCallback!" << std::endl;
	callback();
	std::cout << "End FuncCallback!" << std::endl;
}

auto callback_handler = [&](){
	std::cout << "This is callback_handler";
};

DataCallback(callback_handler);

  • 在QT中的应用
QTimer *timer=new QTimer;
timer->start(1000);
QObject::connect(timer,&QTimer::timeout,[&](){
        qDebug() << "Lambda表达式";
});

int a = 10;
QString str1 = "汉字博大精深";
connect(pBtn4, &QPushButton::clicked, [=](bool checked){
	qDebug() << a <<str1;
	qDebug() << checked;
	qDebug() << "Hua Windows Lambda Button";
});

posted @ 2025-04-07 15:58  njit-sam  阅读(20)  评论(0)    收藏  举报