可调用对象(callable object),即可以対其使用()调用运算符。C++的可调用对象有:

    • 函数
    • 函数指针
    • 函数对象(重载调用运算符)
    • lambda表达式

    一个lambda表达式表示一个可调用的代码单元,可以将其理解为一个未命名的内联函数(匿名函数),可以定义在函数内部。
    编译阶段,编译器会根据lambda生成一个新的类类型。

    1. auto lambda = [capture_list](parameter_list) mutable - > return_type
    2. {
    3. //function_body
    4. }
    5. //下面是对每个部分的详细介绍。
    6. //capture_list: 捕获列表,花括号[]括起。
    7. // 包含函数体使用到的“父函数变量”,即定义在lamba所在函数中的非static局部变量。
    8. // 在“父函数”外部的变量,不需要包含。
    9. // 可为空,表示没有使用到“父函数”的局部变量。
    10. // 捕获方式: 局部变量是何种方式传递进来,值传递或引用传递。
    11. // 值传递: [arg]形式,变量必须能拷贝,且在lambda创建时拷贝,而不是调用时。所以在创建lambda后,
    12. // 外部修改变量不会影响lambda内对应的值。lambda内部也无法修改这些值,必须mutable声明
    13. // 引用传递: [&arg]形式,和函数引用传递一样,要注意变量在调用时是否被失效。
    14. // 隐式捕获: [=]或[&]形式,由编译器来推断我们使用到的局部变量是值传递或者引用传递。
    15. // 混合捕获: [=,arg1,...,argn]形式,arg1~argn是引用传递,其他值传递。
    16. // [&,arg1,...,argn]形式,arg1~argn是值传递,其他引用传递。
    17. // [=,&]、[arg1,=]、[arg1,&]都是错误的
    18. //parameter_list:形参列表,不能有默认实参
    19. //function_body:函数体,可无需通过捕获列表访问static变量和“父函数”以外的名字。
    20. //mutable: 可修改模式,捕获列表中值传递的变量,在lambda内部是无法被修改的,mutable之后,就能修改。
    21. // 不过也只是修改lambda内部的副本,并不会影响外部的。
    22. //return_type:返回类型,必须使用尾置返回。
    23. // 为空则默认,默认机制如下:
    24. // 如果函数体就是1个return语句,自动推断返回类型。
    25. // 否则,返回类型是void
    26. // 返回lambda,则该lambda的捕获不能有引用传递。

    lambda本质上是一个函数对象,即一种类类型。

    1. auto lambda = [capture_list](parameter_list) mutable - > return_type
    2. {
    3. //function_body
    4. }
    5. // 上下本质上是一样的
    6. class lambda {
    7. public:
    8. lambda(int v_a, int v_b); // capture_list: [a, b]
    9. lambda(int& r_a, int &r_b); // capture_list: [&a, &b]
    10. // 返回类型int,对应尾置声明 -> int
    11. int operator(int param1, int param2){ parameter_list: (int param1, int param2)
    12. }
    13. private:
    14. // 值传递的成员,注意是const,即类内部是不能改变成员的值。
    15. // 注:const成员必须在初始化列表初始化
    16. const int v_a;
    17. const int v_b;
    18. // 显式声明了mutable
    19. int v_a;
    20. int v_b;
    21. // 引用传递的成员
    22. // 注:引用成员必须在初始化列表初始化,且不能有默认构造函数。
    23. int& r_a;
    24. int& r_b;
    25. }

    例子:

    1. void fcn3()
    2. {
    3. size_t v1 = 1, v2 = 2, v3 = 3; // “父函数”局部变量
    4. static fuck = 0;
    5. auto f = [&v2, v1] // v2:引用传递,v1:值传递
    6. (int a, int b = 1) // 错误:不能有默认实参
    7. mutable // v1可自修改
    8. {
    9. ++v3; // 错误:没有捕获。
    10. ++fuck; // 正确:可直接访问static变量
    11. ++v2; // 正确,只要v2不是const size_t类型。
    12. ++v1; // 正确,有mutable声明
    13. cout << v1; // 正确,只要include了iostream
    14. return v1; // 错误,默认机制下,被认为是void,应该加上尾置声明-> size_t。
    15. return []{return 42}; // 错误:返回lambda不能有v2引用传递。
    16. };
    17. v1 = 0;
    18. auto j1 = f(); // j1 = 2
    19. auto j2 = v2; // j2 = 3
    20. }