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对象中,否则会在堆上分配动态内存。

posted @ 2024-02-06 19:19  trashwin  阅读(63)  评论(0编辑  收藏  举报