文章目录
- 语法
- 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
运行结果:
可以将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
如果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;
}
输出:
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;
输出
性能
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