c++ function
可调用对象
- 函数
- 函数指针
- 函数类
- lamda表达式
- bind对象
函数类
重载了函数调用运算符()的类的对象,称为函数对象,也叫仿函数。
class callable
{
public:
void operator() (int x) const
{
cout << "callable::operator(): " << x << endl;
}
};
int main(){
callable c;
c(0);
}
相对于普通函数,函数类可以保存状态,有自己的类型。
lambda表达式
capture mutable exception attribute -> ret
- capture: 捕获列表,声明外部变量
- params: 形参列表,可以省略无参数
- mutable: 是否可以修改捕获的变量,可以省略捕获列表不能修改
- exception: 异常设定
- attribute: 属性
- ret: 返回值类型,可以省略,根据body的return推断
- body: 函数体
捕获
值捕获
被捕获的变量的值在lambda表达式创建时拷贝,不会受到外部变量的影响,同样,lambda表达式的修改也不会影响外部变量。
int main()
{
int a = 0;
auto f1 = [a] () { cout << a << endl; }; // 值捕获
auto f2 = [&a] () { cout << a << endl; }; // 引用捕获
auto f3 = [=] () { cout << a << endl; }; // 值捕获
auto f4 = [&] () { cout << a << endl; }; // 引用捕获
a++;
f1();
f2();
f3();
f4();
}
/*
0
1
0
1
*/
引用捕获
捕获的变量就是引用绑定的对象。
隐式捕获
不指定捕获列表,编译器会根据使用的外部变量自动推断捕获方式。
- [=] 按值捕获
- [&] 按引用捕获
混合方式
[=, &x] 按值捕获除x外的所有变量,x按引用捕获
[&, x] 按引用捕获除x外的所有变量,x按值捕获
mutable
按值传递时,默认情况下,lambda表达式不能修改捕获的变量,加上mutable关键字后,可以修改捕获的变量,但仍不影响外部变量。
形参
不支持默认参数,不支持可变参数
bind对象
bind可以绑定一个可调用对象和一组参数,返回一个新的可调用对象。
auto newCallable = bind(callable, arg_list);
arglist是参数列表,可以是占位符,也可以是具体的值。占位符_i表示newCallable的第i个参数。
预先绑定的参数通过值传递,占位符参数通过引用传递。
bind绑定类成员函数时,参数依次为成员函数指针(显式使用&,编译器对于成员函数不会自动转换),对象指针,成员函数参数。
关于lamda和bind的实现
二者底层实现都是仿函数
对lamda表达式来说,编译器会生成一个匿名类,重载了operator(),并且将捕获的变量作为成员变量。
而bind是调用其他函数的,因此还需要记录被调用函数的指针。
因此对于lamda表达式和bind对象使用sizeof,会得到对应类的大小。
std::function
模板类,抽象了函数参数和返回值,相当于可调用对象的包装器。
以上介绍的所有具有相同参数和返回值的函数对象都可以用同一类std::function表示,从而使用统一的处理方式。
一个例外时不能容纳类成员函数,因为类成员函数有一个隐藏的this指针。
可以推迟函数的执行,经常用于回调函数。
function在标准库的实现中,会有小对象优化,当函数对象不超过16字节(两个指针)时,并且要满足时trivally copiable会直接存储在function对象中,否则会在堆上分配动态内存。