lambda
表达式是一种匿名的函数,需先了解C++
中的functor
是什么。
# 1.C++中的functor
要理解C++
中的functor
,可以先看一个例子。
class TestFunctor{
public:
TestFunctor();
~TestFunctor();
void operator()(std::string s) {
cout << s << endl;
}
};
int main() {
TestFunctor A;
A("Hello Functor!");
// Hello Functor!
return 0;
}
如上面这段程序,在类TestFunctor
中通过重载()
运算符,可以使的TestFunctor
类的实例像函数一样被调用,这正是functor
的核心思想。functor
中文可称之为仿函数
,是任何像函数一样的函数。之所以引入functor
是因为functor
不是简单的普通函数,有着比operator()
更多的特征,functor
可以有状态,如类TestFunctor
可以有成员,functor
还区分于普通函数的函数签名。
# 1.1 functor是可参数化的函数
也就是说可以通过传入参数来定制functor
,如下代码
class TestFunctor{
public:
TestFunctor();
TestFunctor(int v) {_value = v;};
~TestFunctor();
void operator()(std::string s) {
cout << _value << " " << s << endl;
}
private:
int _value;
};
int main() {
TestFunctor A(10);
A("Hello Functor!");
// 10 Hello Functor!
A("Hi Functor!");
// 10 Hi Functor!
TestFunctor A(20);
A("Hello Functor!");
// 20 Hello Functor!
A("Hi Functor!");
// 20 Hi Functor!
return 0;
}
通过类A
可以传入参数,构造不同的functor
。到这里还看不出来这种做法的真正妙处。假如现在有个需求,要给数组的每个元素加上10
, 可通过以下方式:
inline void add10(int x) {
cout << x + 10 << " ";
}
int main() {
std::vector<int> v = { 1, 2, 3, 4, 5 };
for_each(v.begin(), v.end(), test::functor::add10);
std::cout << endl; // 11, 12, 13, 14, 15
return 0;
}
不过,上述的方式是通过硬编码实现的,换一个加的值就要重新写一个函数,十分不方便。当然,可以使用模板的方式来做,如下:
template<int n>
inline void add(int x) {
cout << x + n << " ";
}
int main() {
std::vector<int> v = { 1, 2, 3, 4, 5 };
for_each(v.begin(), v.end(), test::functor::add<10>);
std::cout << endl; // 11, 12, 13, 14, 15
return 0;
}
不过,模板函数的方式是在编译时需要确定参数和类型,因此其必须时const
类型,不能传参数进去,也就是像如下方式,程序运行时会报错
int n = 10;
for_each(v.begin(), v.end(), test::functor::add<n>);
这正是functor
可以派上用场的时候:
class TestFunctor{
public:
TestFunctor();
TestFunctor(int v) {_value = v;};
~TestFunctor();
void operator()(std::string s) {
cout << _value << " " << s << endl;
}
private:
int _value;
};
int main()
{
int val = 2;
v = { 1, 2, 3, 4, 5 };
for_each(v.begin(), v.end(), TestFunctor(val));
std::cout << endl;
// 3, 4, 5, 6, 7
}
# 1.2 STL中的functor
二元算术运算
functor
,如plus, minus, multiplies, divides, modulus
二元关系运算
functor
,如equal_to, not_equal_to, greater, greater_equal, less, less_equal
二元逻辑运算
functor
,如logical_and, logical_or
functional
中的一元functor
,如logical_not
等
// logical_not example
#include <iostream> // std::cout, std::boolalpha
#include <functional> // std::logical_not
#include <algorithm> // std::transform
int main () {
bool values[] = {true,false};
bool result[2];
std::transform (values, values+2, result, std::logical_not<bool>());
std::cout << std::boolalpha << "Logical NOT:\n";
for (int i=0; i<2; i++)
std::cout << "NOT " << values[i] << " = " << result[i] << "\n";
return 0;
}
# 1.3 参数绑定
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
vector<int> s = { 1, 2, 3, 4, 5 };
vector<int> v;
std::transform(s.begin(), s.end(), // source
std::back_inserter(v), // destination
std::bind(std::multiplies<int>(), std::placeholders::_1, 100) // C++11
);
for(auto &i : v) {
cout << i << " ";
}
// 100 200 300 400 500
cout << endl;
return 0;
}
如上代码中,transform
函数需要一个一元functor
,而std::multiplies
是二元算术运算functor
,因此需要使用bind
函数给参数绑定参数。知道了bind
函数后,前面介绍的函数参数化问题可通过如下方式解决:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // std:: bind
void addNumber(int i, int number)
{
std::cout << i + number << std::endl;
}
int main()
{
std::vector v = { 1, 2, 3, 4, 5 };
for_each(v.begin(), v.end(),
std::bind(addNumber,
std::placeholders::_1, 10)); // 11 12 13 14 15
}
# 2.lambda表达式
了解了functor
,再来了解lambda
表达式就比较简单了,labmda
是匿名的functor
语法糖以使得functor
的定义更为容易。
#include<iostream>
using namespace std;
int main() {
auto plus = [data=1](const int value)
{
return value + data;
};
cout<< (plus(2) == 3) << endl;
}
值得注意的是capture initializer [data=1]
是C++14
才支持的。lambda
可以通过[]
中传入参数来捕获变量以创建或初始化functor
的成员变量。可以把lambda
当成对象,[]
传入的就是对象的成员变量。借助lambda
,之前的例子可以写为:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // std:: bind
using namespace std;
int main() {
v = { 1, 2, 3, 4, 5 };
int nu = 12;
for_each(v.begin(), v.end(), [value=nu](int x){ cout << x + value << " "; });
std::cout << endl;
// 13 14 15 16 17
return 0;
}
默认的()
重载带const
,c++在函数后加const的意义是,定义的类的成员函数中,常常有一些成员函数不改变类的数据成员,也就是说,这些函数是"只读"函数,而有一些函数要修改类数据成员的值。如果把不改变数据成员的函数都加上const关键字进行标识,显然,可提高程序的可读性。
int operator()(const int value) const
{
return value + data;
}
因此无法修改成员值:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // std:: bind
using namespace std;
int main() {
v = { 1, 2, 3, 4, 5 };
int nu = 12;
for_each(v.begin(), v.end(), [value=nu](int x){ cout << x + value << " "; value++});
std::cout << endl;
// 13 14 15 16 17
return 0;
}
可在lambda
上稍事修改:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // std:: bind
using namespace std;
int main() {
v = { 1, 2, 3, 4, 5 };
int nu = 12;
for_each(v.begin(), v.end(), [value=nu](int x) mutable { cout << x + value << " "; value++});
std::cout << endl;
// 13 14 15 16 17
return 0;
}
当不时使用capture variables
时,可以将lambda
当作函数的参数:
#include <iostream>
inline int plus(int x, int (*getValue)()) {
return getValue() + x;
}
int main()
{
auto getValue = [](){ return 1;};
int res = plus(100, getValue);
std::cout <<"get value: " << res << endl;
}