目录
- 一. lambda 表达式
- 1. lambda 表达式的语法
- 1. lambda 表达式的使用
- 2. lambda 表达式的捕捉列表
- 二. 包装器
- 三. bind 绑定
一. lambda 表达式
Lambda 表达式是 C++11 标准引入的一种新特性, 它提供了一种方便的方式来定义匿名函数.
lambda 表达式实际上是一个匿名的仿函数; 不同于仿函数的是, 该类由编译器自动创建. 而调用lambda 表达式就相当于调用类的 operator() 函数.
1. lambda 表达式的语法
// 捕捉列表 参数列表 返回值类型 函数体
[/*capture-list*/] (/*parameters*/) mutable -> /*return_type*/ {/*statement*/};
-
[ ]: capture-list 捕捉列表, 该列表总是出现在 lambda 函数的开始位置, 编译器根据 [] 来判断接下来的代码是否为 lambda 函数, 捕捉列表能够捕捉上下文中的变量供 lambda 函数使用; 捕捉列表不可省略, 但可以为空.
-
( ): parameters 参数列表, 与普通函数的参数列表一致, 若不需要参数传递, 则可以连同 () 一起省略, 但通常不建议省略.
-
mutable: 在默认情况下, lambda 函数总是一个 const 函数, mutable 可以取消其常量性; (使用该修饰符时, 参数列表不可省略, 即使参数为空).
-
->: 后面接 return_type 返回值类型, 若没有返回值或返回值类型明确的情况下可以省略, 由编译器对返回类型自动推导.
-
{ }: statement 函数体, 和普通函数的函数体一样, 不过在该函数体内, 除了可以使用其参数外, 还可以使用被 [ ] 所捕获的变量.
-
注: 捕捉列表 [ ] 和 函数体 { } 不可省略; 若使用了 mutable 关键字 或 显示声明 -> return 返回值, 就不可以省略 ( ) 参数列表, 即使为空.
1. lambda 表达式的使用
lambda 表达式的类型是由编译器自动生成的, 并且带有随机值, 是无法具体的写出 lambda 表达式的类型, 并且 lambda 表达式是一个匿名的函数对象(匿名对象生命周期只有一行), 若不立即使用 或 使用 auto 推导赋值给对象, 则会销毁.
int main()
{// 显示声明返回值int ret = [](int i, int j)->int { return i + j; }(1, 2);cout << ret << endl;// 自动推导返回值auto add = [](int i, int j) { return i + j; };cout << add(1, 2) << endl;return 0;
}
2. lambda 表达式的捕捉列表
lambda 表达式的使用相较于普通函数和仿函数, 增加了捕捉列表;
捕捉列表可以使 lambda 表达式捕捉父作用域中 lambda 表达式之前的所有变量.
- 传值捕捉 [var]
捕捉变量 var 的拷贝, 传值捕捉到的参数默认是被 const 修饰的, 若希望修改, 需要使用 mutable 修饰.
int main()
{int i = 1;auto add = [i](int j)mutable{ i = j;return i;};cout << add(2) << endl;return 0;
}
- 传引用捕捉 [&var]
捕捉变量 var 的引用
- 传值捕捉所有 [=]
捕捉所有变量的引用
- 传值捕捉所有 [&]
捕捉所有变量的拷贝
- 混合捕捉
捕捉所有(或部分)变量的拷贝(或引用)
注:
- 父作用域是指包含 lambda 函数的语句块, 捕捉列表可以捕捉父作用域中位于 lambda 函数之前定义的所有变量;
- lambda 表达式的捕捉列表不允许变量重复传递, 否则就会导致编译错误;
- 在块作用域中的lambda函数仅能捕捉父作用域中的局部变量, 捕捉任何非此作用域或者非局部变量都会导致编译报错;
- lambda 表达式之间不能相互赋值.
二. 包装器
包装器是一种特殊的类模板, 是适配器的一种, 可以将相同或类似使用方式, 但类型不同的对象, 包装为同一类型, 使其可以达到类似回调函数的效果.
例:
下列的函数, 仿函数, lambda 表达式的使用方式相同, 但却会实例化出不同类型的 useF() 函数.
void func(int i)
{i++;
}struct Functor
{void operator()(int i){i++;}
};template<class F>
void useF(F f, int i)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;f(i);
}int main()
{int i = 0;// 函数指针useF(func, i);// 仿函数useF(Functor(), i);// lambda 表达式useF([](int i) { i++; }, i);return 0;
}
C++11 中增加了 function 包装器解决了这类问题, function 包装器是使用可变参数模板实现的.
#include <functional>
// 类模板原型
template <class Ret, class... Args> //Ret 被调用函数的返回类型, Args… 被调用函数的形参
class function<Ret(Args...)>;
int main()
{int i = 0;// void 返回值, int 参数类型function<void(int)> f; // 函数指针f = func;useF(f, i);// 仿函数f = Functor();useF(f, i);// lambda 表达式f = [](int i) { i++; };useF(f, i);return 0;
}
function 包装器也可以包装静态成员函数, 只需指明类型.
class Func
{
public:static void func(){}
};int main()
{int i = 0;// void 返回值, 无参数类型function<void()> f;f = Func::func;return 0;
}
若绑定非静态成员函数, 非静态成员函数中有隐藏的 this 指针, 需要将类的对象一起传入.
class Add
{
public:int add(int i, int j){return i + j;}
};int main()
{int i = 0;// int 返回值, Add, int, int 参数类型function<int(Add, int, int)> f;Add a;f = &Add::add;cout << f(Add(), 1, 1) << endl;return 0;
}
也可以传对象的指针, 但必须是左值的对象, 右值是没有地址的.
class Add
{
public:int add(int i, int j){return i + j;}
};int main()
{int i = 0;// int 返回值, Add*, int, int 参数类型function<int(Add*, int, int)> f;Add a;f = &Add::add;cout << f(&a, 1, 1) << endl;return 0;
}
三. bind 绑定
bind 是一个函数模板, 包含在 <functional> 头文件中, bind 可以接受一个可调用对象(callable object), 然后生成一个新的可调用对象来 “适应” 原对象的参数列表.
bind 可以调整可调用对象的参数 – 包括调整参数顺序和调整参数个数.
template <class Fn, class... Args>
bind (Fn&& fn, Args&&... args);
- 参数:
fn: 需要绑定的函数或函数对象的指针;
args: 可变参数包, 是函数或函数对象需要调整的参数, 也可以使用占位符(placeholders)对参数进行占位, 表示该位置的参数需要在调用时再传递进来.
placeholders 是 C++11 引入的一个命名空间, 其中的 _1, _2, …, _N 称为占位符,分别表示函数中的第 1, 2, …, N 位参数, 可以直接使用
例: 调整参数顺序
int func(int i, int j)
{return i - j;
}int main()
{int i = 0;// _2 代表实参的第二个参数, _1 代表实参的第一个参数auto f = bind(func, placeholders::_2, placeholders::_1);// 1 传参给 _1, _1 传参给 j; 2 传参给 _2, _2 传参给 i;cout << f(1, 2) << endl;return 0;
}
可以和包装器配合使用, 调整参数的个数
class Add
{
public:int add(int i, int j){return i + j;}
};int main()
{Add a;int i = 0;// int 返回值, Add, int, int 参数类型function<int(int, int)> f;f = bind(&Add::add, Add(), placeholders::_1, placeholders::_2);cout << f(1, 1) << endl;return 0;
}