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

2023-12-25 09:03:30

0、简介

  • 就地匿名定义目标函数或者对象,不需要额外声明函数或者对象;
  • 可把函数当作参数给别的函数调用;
  • 简洁、高效;
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表达式定义了一个匿名函数,并且可以捕获一定范围内的变量

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

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

    [] () {	// 没有参数, 参数列表为空
        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 示例:类成员方法中

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 示例:普通方法中

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表达式不能通过列表初始化自动推导出返回值类型。
在这里插入图片描述

auto fun = [] () {
   return 1; // OK
};

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

四、option选项

4.1 mutable

在这里插入图片描述

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 普通函数:
void fun1() throw(); // 表示不抛出任何异常
void fun2() throw(int); // 可能抛出int型异常
void fun3() throw(const char * , float); // 可能抛出C风字符串和单精浮点型异常
void fun4() throw(thread); // 可能抛出thread对象异常
2.2.2 Lambda:
[] 	[] () throw(int, string) {};

五、其他注意事项:

5.1 怎么调用匿名函数?

  • 普通函数调用?(函数名(参数列表))
    在这里插入图片描述
  • 在函数后面加上(parameters)
    在这里插入图片描述
[]() {
    cout << 123;
}();

六、Lambda表达式的本质

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

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

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

文章来源:https://blog.csdn.net/qq_31747473/article/details/135190116
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。