定制操作
- 学习STL标准库过程中,了解到许多算法都会比较序列中的元素,甚至有些容器自带都会比较元素的顺序(如SET)
- 一般而言,这类算法使用元素类型的重载运算符(operate <==)来完成元素之间的比较。
- 为了方便我们根据实际需求来完成排序,STL允许我们提供自己定义的操作来代替默认运算符
举个栗子,比方说我们有一个string类型的vector容器,其中含有以下几个单词
“foxes”,“yellow”,“theme” ,“odd”, “slow”
现在,要求按照单词的长短进行排序,且要求从长到短进行排列。解决这个问题,可以用到sort函数进行排序,但是,问题在于如何满足从长到短这个条件,于是我们可以,定义一个可调用的表达式,称为谓词(predicate)进行重载sort函数
- 话不多说,我们来定义这个谓词,上代码
bool check_size(const string &a, const string &b)
{
return (a.size () >= b.size ());
}
很直观,这个谓词就是返回一个布尔值,且接收两个string类型参数,所以也称为二元谓词(binary predicate),当然相对应的,也就有一元谓词(unary predicate)。那怎么使用呢?看下面:
// 定义一个vector存储上面的单词
vector<string> words = { "foxes","yellow","theme" ,"odd", "slow" };
// 调用sort算法,将谓词check_size作为参数传入
stable_sort (words.begin (), words.end (), check_size);
// 打印结果
for (string &s : words)
{
// 打印重排列后的元素
cout << s << " ";
}
输出
yellow theme foxes slow odd
细心的读者发现,这个谓词有点类似于函数指针,都能够作为参数传入另一个函数。但两者又有本质不同,只是形式上有相似性。
lambda表达式
众所皆知,C++11标准引入了lambda表达式,其实质就是一个匿名函数。我们可以方便的用其替代上述谓词。其基本的语法如下:
[捕获列表] (参数列表) {函数体}
- 与普通函数不同,lambda函数没有函数名,取而代之的是捕获列表,能够获取表达式所在函数体的参数,且除了不需指定返回类型外,其他部分与一般函数相同。
那么,我们再来实践一下:
// 将上面的函数替换如下lamda表达式
stable_sort(words.begin(), words.end(),
[](const string &a, const string &b)
{return a.size() >= b.size(); });
可以输出与上面相同的结果,好处就是不用额外定义一个函数啦~
参数绑定
与lambda表达式相对应,标准库提供了一个bind函数供我们个性化使用。其根本设置意图在于适配,因为有的函数要求接受一个一元谓词,而我们定义的函数是二元谓词,但是我们又不想改变这个函数。那么STL给出了解决方案—bind函数。
- 假设定义的函数还是:
bool check_size(const string &a, const string &b)
{
return (a.size () >= b.size ());
}
我们要使用的算法是find_if,即找到一个长度大于等于int sz = 5的元素,若用lambda表达式,很容易写出
int sz = 5;
vector<string>::iterator i = find_if(words.begin() , words.end(),
[sz](const string &a)
{return a.size() >= sz; });
cout << *i << endl;
输出
yellow
- 但是,如果我们坚持要用定义的check_size函数怎么办呢?举个栗子
// 用字符串长度为5代替
string sz = "abcde";
// bind绑定占位符_1到check_size第一个形参
auto compare = bind(check_size, _1, sz);
vector<string>::iterator i = find_if(words.begin(), words.end(), compare);
cout << *i << endl;
输出
yellow
- 结果完全相同有木有,需要注意的是,这里的compare是一个函数对象,可以直接传入find_if函数的形参中。
- 那我们如何直接使用它呢?
string s = "hello";
bool b1 = compare(s);
cout << b1 << endl;
输出
1
其实,bind的作用也很多,例如可以改变形参顺序,或者加入ref()函数可以进行引用绑定(默认值拷贝方式传递)。