C++11 Lambda表达式(匿名函数)


Lambdas是匿名函数的一个花哨的名称。本质上,它们是在代码中的逻辑位置编写函数(如回调)的一种简单方法。
类似: [](){}

std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

语法

Lambdas包含三部分

  • capture list 代码用到需要copy到lambda的参数
  • 参数列表,执行过程传递到lambda的参数
  • 函数代码
int i = 0, j = 1;
auto func = [i, &j](bool b, float f){ ++j; cout << i << ", " << b << ", " << f << endl; };
func(true, 1.0f);
cout <<"&j: "<<j << endl;

上述函数:

  • 通过value获取i,通过引用获取j
  • 接收两个参数 bool bfloat f

运行结果:

0, 1, 1
&j: 2

可以将lambda函数看作类

  • captures是数据成员,lambda函数可以在代码区间访问数据成员
  • 当lambda函数创建,构造器复制获取到的数据到数据成员里
  • 操作符operator()(...)
  • lambda有生命周期和析构器释放变量成员

中括号的语法

  • [&](){} 表示所有lambda函数用到的参数都采用引用的方式
  • [=](){} 表示所有lambda函数用到的参数都采用复制的方式
  • [&, i, j](){} 表示i和j复制值,其他引用
  • [=, &i, &j](){} 表示i和j通过引用的方式,其他复制

Lambda的类型

lambda函数并不是标准的std::function,但可以把lambda函数赋给一个std::function
获取lambda函数的唯一方式是auto

auto f2 = [](){};

如果capture list为空,可以把lambda函数转为一个C类型的函数指针

void (*foo)(bool, int);
foo = [](bool, int){};

Lambda的作用范围

所有的captured变量的作用范围是在lambda函数内

#include <iostream>
#include <functional>

struct MyStruct {
	MyStruct() { std::cout << "Constructed" << std::endl; }
	MyStruct(MyStruct const&) { std::cout << "Copy-Constructed" << std::endl; }
	~MyStruct() { std::cout << "Destructed" << std::endl; }
};

int main() {
	std::cout << "Creating MyStruct..." << std::endl;
	MyStruct ms;
	
	{
		std::cout << "Creating lambda..." << std::endl;
		auto f = [ms](){}; // note 'ms' is captured by-value
		std::cout << "Destroying lambda..." << std::endl;
	}

	std::cout << "Destroying MyStruct..." << std::endl;
}

执行结果:

Creating MyStruct...
Constructed
Creating lambda...
Copy-Constructed
Destroying lambda...
Destructed
Destroying MyStruct...
Destructed

可变 lmabdas

lambda函数的()操作符默认const,它不能修改通过赋值传入的值,可以通过mutable修改

int i = 1;
[&i](){ i = 1; }; // ok, 'i' is captured by-reference.
[i](){ i = 1; }; // ERROR: assignment of read-only variable 'i'.
[i]() mutable { i = 1; }; // ok.

lambda复制的值与类类似

int main() {
    int i = 0;
    auto x = [i]() mutable { cout << ++i << endl; };
    x();
    auto y = x;
    x();
    y();
    cout << i << endl;
}

输出:

1
2
2
0

Lambda的大小

因为lambda函数有capture list,所以它的大小不唯一

auto f1 = [](){};
cout << sizeof(f1) << endl;

std::array<char, 100> ar;
auto f2 = [&ar](){};
cout << sizeof(f2) << endl;

auto f3 = [ar](){};
cout << sizeof(f3) << endl;

输出

1
8
100

性能

lambda函数是对象而不是指针,可以被编译器方便的inline,更像仿函数(functors)。
多次调用lambda函数(例如在std::sort或者std::copy_if)相比如全局函数更快。这是一个C++比C更快的样例。

std::function

std::function是用于存储和调用可调用类型的一个模板对象,例如函数、对象、lambda函数和std::bind的结果

#include <iostream>
#include <functional>
using namespace std;

void global_f() {
	cout << "global_f()" << endl;
}

struct Functor {
	void operator()() { cout << "Functor" << endl; }
};

int main() {
	std::function<void()> f;
	cout << "sizeof(f) == " << sizeof(f) << endl;

	f = global_f;
	f();

	f = [](){ cout << "Lambda" << endl;};
	f();

	Functor functor;
	f = functor;
	f();
}

输出:

sizeof(f) == 64
global_f()
Lambda
Functor

std::function的大小

在clang++,std::function的大小(忽略返回值和参数)是32字节,它通过最小优化,更像是std::string所做的。这意味着小的对象,std::function可以把他们作为内存的一部分,但是对于更大的对象则需要动态开辟一块内存。这是一个在64位机器的执行样例。

#include <iostream>
#include <functional>
#include <array>
#include <cstdlib> // for malloc() and free()
using namespace std;

// replace operator new and delete to log allocations
void* operator new(std::size_t n) {
	cout << "Allocating " << n << " bytes" << endl;
	return malloc(n);
}
void operator delete(void* p) throw() {
	free(p);
}

int main() {
	std::array<char, 16> arr1;
	auto lambda1 = [arr1](){}; 
	cout << "Assigning lambda1 of size " << sizeof(lambda1) << endl;
	std::function<void()> f1 = lambda1;

	std::array<char, 200> arr2;
	auto lambda2 = [arr2](){}; 
	cout << "Assigning lambda2 of size " << sizeof(lambda2) << endl;
	std::function<void()> f2 = lambda2;
}

输出

Assigning lambda1 of size 16
Assigning lambda2 of size 200
Allocating 208 bytes

原文中作者提到std::function动态分配的阈值为17,尝试了下我的编译器阈值大于17,lanbda对象内存需要是连续的。
Under the hood of lambdas and std::function

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值