c++11新增了std::function
、std::bind
、lambda
表达式等封装使函数调用更加方便。
std::function
讲std::function
前首先需要了解下什么是可调用对象
满足以下条件之一就可称为可调用对象:
而std::function
就是上面这种可调用对象的封装器,可以把std::function
看做一个函数对象,用于表示函数这个抽象概念。std::function
的实例可以存储、复制和调用任何可调用对象,存储的可调用对象称为std::function
的目标,若std::function
不含目标,则称它为空,调用空的std::function
的目标会抛出std::bad_function_call
异常。
使用参考如下实例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| std::function<void(int)> f; #include <functional> #include <iostream>
struct Foo { Foo(int num) : num_(num) {} void print_add(int i) const { std::cout << num_ + i << '\n'; } int num_; };
void print_num(int i) { std::cout << i << '\n'; }
struct PrintNum { void operator()(int i) const { std::cout << i << '\n'; } };
int main() { std::function<void(int)> f_display = print_num; f_display(-9);
std::function<void()> f_display_42 = []() { print_num(42); }; f_display_42();
std::function<void()> f_display_31337 = std::bind(print_num, 31337); f_display_31337();
std::function<void(const Foo&, int)> f_add_display = &Foo::print_add; const Foo foo(314159); f_add_display(foo, 1); f_add_display(314159, 1);
std::function<int(Foo const&)> f_num = &Foo::num_; std::cout << "num_: " << f_num(foo) << '\n';
using std::placeholders::_1; std::function<void(int)> f_add_display2 = std::bind(&Foo::print_add, foo, _1); f_add_display2(2);
std::function<void(int)> f_add_display3 = std::bind(&Foo::print_add, &foo, _1); f_add_display3(3);
std::function<void(int)> f_display_obj = PrintNum(); f_display_obj(18); }
|
从上面可以看到std::function
的使用方法,当给std::function
填入合适的参数表和返回值后,它就变成了可以容纳所有这一类调用方式的函数封装器。std::function
还可以用作回调函数,或者在C++里如果需要使用回调那就一定要使用std::function
,特别方便。
std::bind
使用std::bind
可以将可调用对象和参数一起绑定,绑定后的结果使用std::function
进行保存,并延迟调用到任何需要的时候。
std::bind
通常有两大作用:
- 将可调用对象与参数一起绑定为另一个
std::function
供调用
- 将n元可调用对象转成m(m < n)元可调用对象,绑定一部分参数,这里需要使用
std::placeholders
具体示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include <functional> #include <iostream> #include <memory>
void f(int n1, int n2, int n3, const int& n4, int n5) { std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << std::endl; }
int g(int n1) { return n1; }
struct Foo { void print_sum(int n1, int n2) { std::cout << n1 + n2 << std::endl; } int data = 10; };
int main() { using namespace std::placeholders;
int n = 7; auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n); n = 10; f1(1, 2, 1001);
auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5); f2(10, 11, 12);
Foo foo; auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1); f3(5);
auto f4 = std::bind(&Foo::data, _1); std::cout << f4(foo) << std::endl;
std::cout << f4(std::make_shared<Foo>(foo)) << std::endl; }
|
lambda
表达式
lambda表达式可以说是c++11引用的最重要的特性之一,它定义了一个匿名函数,可以捕获一定范围的变量在函数内部使用,一般有如下语法形式:
1
| auto func = [capture] (params) opt -> ret { func_body; };
|
其中func
是可以当作lambda
表达式的名字,作为一个函数使用,capture
是捕获列表,params
是参数表,opt
是函数选项(mutable之类), ret是返回值类型,func_body是函数体。
一个完整的lambda表达式:
1 2 3
| auto func1 = [](int a) -> int { return a + 1; }; auto func2 = [](int a) { return a + 2; }; cout << func1(1) << " " << func2(2) << endl;
|
如上代码,很多时候lambda表达式返回值是很明显的,c++11允许省略表达式的返回值定义。
lambda
表达式允许捕获一定范围内的变量:
[]
不捕获任何变量
[&]
引用捕获,捕获外部作用域所有变量,在函数体内当作引用使用
[=]
值捕获,捕获外部作用域所有变量,在函数内内有个副本使用
[=, &a]
值捕获外部作用域所有变量,按引用捕获a变量
[a]
只值捕获a变量,不捕获其它变量
[this]
捕获当前类中的this指针
lambda表达式示例代码:
1 2 3 4 5 6
| int a = 0; auto f1 = [=](){ return a; }; cout << f1() << endl;
auto f2 = [=]() { return a++; }; auto f3 = [=]() mutable { return a++; };
|
代码中的f2是编译不过的,因为修改了按值捕获的外部变量,其实lambda表达式就相当于是一个仿函数,仿函数是一个有operator()
成员函数的类对象,这个operator()
默认是const
的,所以不能修改成员变量,而加了mutable
,就是去掉const
属性。
还可以使用lambda表达式自定义stl的规则,例如自定义sort排序规则:
1 2 3 4 5 6 7 8 9
| struct A { int a; int b; };
int main() { vector<A> vec; std::sort(vec.begin(), vec.end(), [](const A &left, const A &right) { return left.a < right.a; }); }
|
总结
std::function
和std::bind
在平时编程过程中封装函数更加的方便,而lambda表达式将这种方便发挥到了极致,可以在需要的时间就地定义匿名函数,不再需要定义类或者函数等,在自定义STL规则时候也非常方便,让代码更简洁,更灵活,提高开发效率。