文章目录

  • ​​语法​​
  • ​​Lambda的类型​​
  • ​​Lambda的作用范围​​
  • ​​可变 lmabdas​​
  • ​​Lambda的大小​​
  • ​​性能​​
  • ​​std::function​​
  • ​​std::function的大小​​


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 b​​​和​​float 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