【C++11新特性】Lambda表达式

0、简介

  • 就地匿名定义目标函数或者对象,不需要额外声明函数或者对象;
  • 可把函数当作参数给别的函数调用;
  • 简洁、高效;
c 复制代码
vector<int> v{1, 2, 3};
auto f = [](int a, int b) {
    return a > b;
};
sort(v.begin(), v.end(), f);
for (auto i : v) {
    cout << i << " ";
}

一、基本用法

lambda表达式定义了一个匿名函数,并且可以捕获一定范围内的变量

c 复制代码
/**
capture:捕获列表
paramater:参数列表
option:函数选项
returnType:返回值
body:函数体
*/
[capture](paramater) option -> returnType {body}; 
  1. 捕获列表[]: 捕获一定范围内的变量

  2. 参数列表():和普通函数的参数列表一样,如果没有参数列表可以省略

    c 复制代码
    [] () {	// 没有参数, 参数列表为空
        return 1;
    }
    [] { // 没有参数, 参数列表省略不写
        return 1;
    }
  3. option选项:可以省略

    • mutable:可以修改按值传递进来的拷贝(修改的是副本)
    • exception:指定函数抛出的异常(throw(int, string...)
  4. 返回值类型:C++ 11中,lambda表达式的返回值是通过返回值后置语法来定义的。(编译器可以自动推导出返回值是什么类型的)

  5. 函数体:函数的实现,不可省略,可以为空。

二、捕获列表

  1. [] - 不捕捉任何变量

  2. [&] - 捕获外部作用域中所有变量,并作为引用在函数体内部使用(引用传递

  3. [=] - 捕获外部作用域中所有变量,并作为副本在函数体内使用(按值捕获

    • 拷贝的副本在匿名函数体内部是只读的
  4. [=, &val] - 按值捕获外部作用域中所有变量,并按照引用捕获外部变量val

  5. [val] - 按值捕获val变量,同时不捕获其他变量

  6. [&val] - 引用捕获val变量,同时不捕获其他变量

  7. [this] - 捕获当前类中的this指针

    • lambda表达式拥有和当前类成员函数同样的访问权限

    • 如果已经使用了&或者=,默认添加此选项

2.1 示例:类成员方法中

c 复制代码
class Test {
public:
    void output(int x, int y){
//        [] () { // 没有捕获外部变量,不能使用类成员 number
//        	return number; // [Error] 'this' was not captured for this lambda function
//		};

		[=] () { // 值拷贝方式捕获
			return number + x + y; // OK
		};

		[&] () { // 引用的方式捕获
			return number + x + y; // OK
		};
		
		[this] () { // 捕获this指针,可以访问对象内部成员
			return number; // OK
    	};

//    	[this] () { // 捕获this指针,可访问类内部成员,没有捕获到变量x,y,因此不能访问。
//    		return number + x + y; // Error
//		};

		[this, x, y] () { // 捕获this、x、y
			return number + x + y; // OK
		};

		[this] () { // 捕获this指针,可以修改对象内部变量的值
			return number++; // OK
		};
	};

private:
    int number = 100;
};

2.2 示例:普通方法中

c 复制代码
int main() {
    int a = 1, b = 2;

    [] () { // 未捕获任何变量
		return a;
	};
   
    [&] () { // 所有外部变量-引用
		rturn a++; // OK
	};
   
    [=] () { // 所有外部变量-只读
		return a; // OK
	};
   
    [=] () { // 所有外部变量-只读
		return a++; // error
	};
   
    [a] () { // a只读
		return a + b;  // error
	};
   
    [a, &b] () { // a只读,b引用
		return a + (b++); // OK
	};
   
    [=, &b] () { // b引用,其他只读
		return a + (b++); // OK
	};
}

三、返回值

lambda表达式的返回值一般都是非常明显的,因此在C++11中允许省略lambda表达式的返回值

// 完整的lambda定义:
auto fun = [] (int parameter) -> int {
    return parameter + 1;
};

// 简化:
auto fun = [] (int parameter) {
    return parameter + 1;  
};

​ 一般情况下,不指定lambda表达式的返回值,编译器会根据return语句自动推导返回值的类型,但需要注意的是lambda表达式不能通过列表初始化自动推导出返回值类型。

c 复制代码
auto fun = [] () {
   return 1; // OK
};

auto fun = [] () { // 有多种情况,结构体列表?整形列表?
   return {1, 2}; //[Error] returning initializer list
};

四、option选项

4.1 mutable

c 复制代码
void test(int x, int y) {
	int a;
	int b;
	[=, &x] () {
		int c = a;
		int d = x;
		b++; // error
	};
}

void test(int x, int y) {
	int a;
	int b;
	[=, &x] () mutable { 
		int c = a;
		int d = x;
		b++; // success
	};
}

4.2 exception

  • 如果有一个函数(可能来自第三方库)会抛出异常,怎么知道它可能抛出的类型呢?
    • 查看源码?如果只有函数声明,没有函数实现呢?
  • 异常规格说明,是用于说明函数可能抛出的异常种类,作为函数声明的修饰符,跟在参数列表(parameters)后面,与静态函数说明const放在一起。
4.2.1 普通函数:
c 复制代码
void fun1() throw(); // 表示不抛出任何异常
void fun2() throw(int); // 可能抛出int型异常
void fun3() throw(const char * , float); // 可能抛出C风字符串和单精浮点型异常
void fun4() throw(thread); // 可能抛出thread对象异常
2.2.2 Lambda:
c 复制代码
[] 	[] () throw(int, string) {};

五、其他注意事项:

5.1 怎么调用匿名函数?

  • 普通函数调用?(函数名(参数列表))
  • 在函数后面加上(parameters)
c 复制代码
[]() {
    cout << 123;
}();

六、Lambda表达式的本质

  • 可以看作是一种可调用对象(普通函数指针?仿函数?)

  • C++中Lambda表达式是一个仿函数

    • Lambda表达式的类型在C++11中会被看做一个带operator()的类,即仿函数(重载了operator(),那么这个类对象都可以作为函数来进行调用,称之为仿函数)
    • C++标准中,Lambda表达式的operator() 默认是const的,一个const成员函数是无法修改成员变量值的。
    • mutable关键字的作用就是取消operator()const属性
  • 对于没有捕获任何外部变量的匿名函数,可以转换为普通的函数指针

相关推荐
怀澈12210 分钟前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
chnming198733 分钟前
STL关联式容器之set
开发语言·c++
威桑44 分钟前
MinGW 与 MSVC 的区别与联系及相关特性分析
c++·mingw·msvc
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
yigan_Eins1 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
Mr.131 小时前
什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
开发语言·c++
阿史大杯茶1 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
C++忠实粉丝1 小时前
计算机网络socket编程(3)_UDP网络编程实现简单聊天室
linux·网络·c++·网络协议·计算机网络·udp
我们的五年2 小时前
【Linux课程学习】:进程描述---PCB(Process Control Block)
linux·运维·c++
程序猿阿伟2 小时前
《C++ 实现区块链:区块时间戳的存储与验证机制解析》
开发语言·c++·区块链