函数对象与lambda表达式有类似之处,lambda表达式实质上属于一种特殊的函数对象。具体特殊在哪里,后文会谈到,可以先知晓这点。接下来先分别了解下函数表达式与lambda表达式吧。
一、函数对象
从实现上来说,函数对象是指实现了operator()的类的对象。可以用于STL算法。在结构或类中实现函数对象时,将比简单函数更有妙用,因为它还可以存储状态有关的信息。
常用于STL算法的函数对象可分为以下两种:
一元函数:接受一个参数的函数,如f(x)。如果一元函数返回一个布尔值,则该函数称为谓词(一元谓词)。
二元函数:接受两个参数的函数,如f(x, y)。如果二元函数返回一个布尔值,则该函数称为二元谓词。
示例1
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<typename T>
struct displayKeepCount
{
int count;
// 构造函数
displayKeepCount() :count(0){}
void operator()(const T &element) // struct默认是public的,若写在类中,需要Public修饰
{
++count; // 函数对象另外妙用,可以存储状态
cout << " " << element;
}
};
int main()
{
vector<int> vecInt;
for (int i = 0; i < 10; i++)
{
vecInt.push_back(i);
}
cout << "输出vector的内容:\n";
displayKeepCount<int> result;
result = for_each(vecInt.begin(), vecInt.end(), displayKeepCount<int>()); // 第三个参数为函数对象
cout << "\nresult.count = " << result.count << endl;
system("pause");
return 0;
}
执行结果:
示例2:
// 不区分大小写排序字符串:用到函数对象
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
template<typename T>
void display(const T ¶m)
{
for (auto iter = param.cbegin(); iter != param.cend(); iter++)
{
cout << " " << *iter;
}
cout << "\n\n";
}
// 不区分大小写排序
class CcompareNoCase
{
public:
bool operator()(const string &s1, const string &s2)
{
string s1ToLower;
string s2ToLower;
s1ToLower.resize(s1.size());
s2ToLower.resize(s2.size());
transform(s1.cbegin(), s1.cend(), s1ToLower.begin(), tolower);
transform(s2.cbegin(), s2.cend(), s2ToLower.begin(), tolower);
return (s1ToLower > s2ToLower);
}
};
int main()
{
vector<string> vecStr;
vecStr.push_back("this");
vecStr.push_back("is");
vecStr.push_back("a");
vecStr.push_back("test");
cout << "---L1 原始数据:" << endl;
display(vecStr);
cout << "---L2 直接调用sort后排序:" << endl;
sort(vecStr.begin(), vecStr.end());
display(vecStr);
cout << "---L3 调用自定义排序规则后排序:" << endl;
sort(vecStr.begin(), vecStr.end(), CcompareNoCase());
display(vecStr);
system("pause");
return 0;
}
执行结果:
二、lambda表达式
可将lambda表达式视为实现了公有operator()的匿名结构/类,从这种意义上lambda属于函数对象。
template<typename T>
struct display
{
void operator()(const T &element) const
{
cout << element << " ";
}
};
// 使用函数对象
for_each(vecInt.begin(), vecInt.end(), display<int>());
// 使用lambda表达式
for_each(vecInt.begin(), vecInt.end(), [](int &element){ cout << element << " "; });
// 编译器见到上述lambda表达式时,自动扩展为类似结构体display的表示
lambda表达式以方括号[]开头,告诉编译器接下来是一个lambda表达式,方括号后面是一个参数列表,该参数列表与不使用lambda表达式时提供给operator()的参数列表相同。
注意:除非使用mutable指定,否则不能修改状态变量。
// 一元函数对应的lambda表达式
[](Type paramName){ /*other code*/ }
[](Type ¶mName){ /*other code*/ }
// 一元谓词对应的lambda表达式如
[](const int &num){ return (num % 2) == 0; }
// 通过捕获列表接收状态变量的lambda表达式
// 如在数字能被用户输入值整除时返回true
auto idx = find_if(vecInt.begin(),
vecInt.end(),
[divisior](const int ÷nd){ return (dividend % divisor) == 0; });
// lambda表达式通用语法
// 带状态变量
[stateVar1, stateVar2](Type ¶m){ /*other code*/ }
// 要修改状态变量,加mutable关键字,此种修改只在lambda内部有效
[stateVar1, stateVar2](Type ¶m) mutable { /*other code*/ }
// 要使得在lambda内部修改的状态变量在外部有效,声明为引用
[stateVar1&, stateVar2&](Type ¶m){ /*other code*/ }
// 向编译器指定返回类型,可使用->
[stateVar1, stateVar2](Type param1, Type param2) -> ReturnType { return (value or expression); }
// 复合语句还可以包含由多个分号隔开的语句 --- 如果lambda包含多行代码,必须显示指定返回类型
[stateVar1, stateVar2](Type param1, Type param2) ->
ReturnType { statement1; statement2; return (value or expression); }