泛型算法
大多数算法都定义在头文件algorithm中,标准库还在头文件numeric中定义了一组泛型算法。
它们可以用于不同类型的元素和多种容器类型(不仅包括标准库类型,如vector或list,还包括内置的数组类型)
只读算法
find(beg,end,val); //前两个参数是迭代器范围,第三个参数是要查找的值
返回第一个等于给定值的元素的迭代器。如果范围中无匹配元素,则返回第二个参数。
所需的头文件是algorithm。
accumulate(beg,end,iniSum) //前两个参数是迭代器范围,第三个参数是和的初始值
注意,元素类型与和初始值的类型不必相同,但是它们之间必须能够执行“+”运算。
所需的头文件是numeric。
对于只读而不改变元素的算法,通常用cbegin()和cend()指定迭代器范围
equal(v1.cbegin(),v1.cend(),v2.cbegin());
v1和v2中元素的类型不必相同,只要我们能用==来比较这两种元素类型即可。
注意,equal基于一种非常重要的假设:它假定第二个序列至少与第一个序列一样长。用单一迭借口代器表示第二个序列的算法都有这个假设。
写容器元素的算法
使用这类算法时,必须确保原序列大小至少不小于我们要求算法写入的元素数目。(算法不会执行容器操作,因此它自身不可能改变容器大小)
fill(v.begin(),v.end(),val) //将迭代器范围内的元素都置为val
fill_n(v.begin(),n,val) //将给定值赋予迭代器指向的元素开始的指定个元素。
back_iterator
back_iterator接受一个指向容器的引用,,返回一个与该容器绑定的插入迭代器。当我们通过此迭代器赋值时,赋值运算会调用push_back将一个具有给定值的元素插入到容器中
用法:
vector
auto it=back_iterator(vec);
*it=42; //容器中现在有一个元素
将back_iterator作为算法的目的位置:
vector
fill_n(back_iterator(vec),10,0) //添加10个元素到vec中(不能用vec.begin()作为迭代器写入元素。因为此时vec中没有元素,是空的。)
copy(v1.begin(),v1.end(),v2.begin()); //前两个参数是一对迭代器范围,第三个参数是目的序列的起始位置
其返回值是指向v2尾元素之后的位置的迭代器
注意,目的序列至少要包含与输入序列一样多的元素。这一点很重要。
replace(v.begin(),v.end(),oldValue,newValue)//前两个参数是一对迭代器范围,算法将该范围内所有等于oldValue的元素的值替换为newValue
replace(v.begin(),v.end(),v2.begin(),oldValue,newValue) //原序列不变,v2包含v1的拷贝,且v2中所有等于oldValue的元素的值替换为newValue
sort(v.begin(),v.end()) //利用元素的<运算符实现从小到大的排序
unique(v.begin(),v.end()) //假如某个元素第一次出现的,那么它的位置保持不变。之后重复出现的该元素都被放在序列末尾。算法返回指向不重复区域之后一个位置的迭代器
定制操作
谓词
谓词是一个函数,接受一个或两个参数。返回bool类型值。接受谓词的算法对输入序列中的元素调用谓词。因此,元素类型必须能转换成谓词的参数类型
例
bool isShorter(const string &s1,const string &s2)
{
return s1.size()
}
stable_sort(v.begin(),v.end(),isShorter); //isShorter使得算法用长度大小排序。而stable_sort与sort不太一样。它能维持相等元素的原有顺序。
lambda表达式
有些算法只能接受一元谓词,有些可以接受二元谓词。有时我们希望的操作需要更多参数,超出了算法对谓词的限制。因此,引用lambda表达式。lambda表达式可能定义在函数内部
lambda表达式具有以下形式
[捕获列表](参数列表)->返回类型{函数体} 例 []{return 42} //该lambda表达式的捕获列表为空,省略了参数列表和返回类型
捕获列表是由若干个定义在lambda所在函数中的局部变量组成的列表(将局部变量包含在其捕获列表中,lambda表达式能够在函数体使用这些变量)
调用lambda表达式
例:
auto f=[]{return 42}; 用lambda表达式定义可调用对象
cout< 例: vector ... stable_sort(v.begin(),b.end(),[](const string &a,const string &b){return a.size() 使用lambda表达式代替谓词时,它的参数个数谓词的参数个数一样。但是它可以访问自己所在函数中的局部变量(要在捕获列表中指出)。还可以直接使用定义在当前函数之外的名字。 lambda表达式的值捕获 注意被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝 void f(){ int n=1; auto f=[n]{return n;}; n=2; auto j=f();//j 是1 } lambda表达式的引用捕获 void f(){ int n=1; auto f=[&n]{return n;}; n=2; auto j=f();//j 是2 } 若函数返回lambda,则lambda不能包含引用捕获。(因为捕获的引用指向的是局部变量,而局部变量在函数结束后就不存在了) 捕获指针,迭代器也是一样,必须确保lambda执行时指针或迭代器指向的对象存在。 隐式捕获 可以让编译器根据lambda的代码来推断我们要用哪些变量 用法:在捕获列表中写一个&或者=.&表示采用引用方式,=表示采用值捕获方式。 void f(){ int n=1; auto f=[=]{return n;}; //隐式捕获,值捕获方式 n=2; auto j=f();//j 是1 } void f(){ int n=1; auto f=[&]{return n;}; //隐式捕获,引用捕获方式 n=2; auto j=f();//j 是2 } void f(){ int n1=1; int n2=3; auto f=[=,&n2]{return n1+n2;}; //n1隐式捕获,值捕获方式;n2显式捕获,引用捕获方式 n1=2; n2=5; auto j=f();//j 是1+5=6 } void f(){ int n1=1; int n2=3; auto f=[&,n2]{return n1+n2;}; //n1隐式捕获,引用捕获方式;n2显式捕获,值捕获方式 n1=2; n2=5; auto j=f();//j 是2+3=5 } 可变lambda 默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果想要改变值拷贝的变量,需要在参数列表后面加上关键字mutable. cout<< n << endl; //输出0 即一个值被拷贝的变量,它的值 依然没有被修改。这维持按值引用的特性。 若lambda函数体中只有单一的return语句,那么无须指定返回类型,编译器会根据return 后面的表达式推断返回类型 若lambda函数体中有多个return语句,那么需要指定返回类型。定义返回类型时,必须使用尾置返回类型 [](int i)->int { if(i<0) return -i; else return i; } bind接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表 用法: auto newCallable=bind(callable,arg_list); arg_list中可能含有形如_n的名字。n指代传递给新生成的可调用对象的参数表中的第n 个参数。例, bool check_size(const string&,string::size_type sz){ return s.size()>=sz; } auto check=bind(check_size,_1,6); string s="hello,you"; 所以bind用来 1.将一个多参数的可调用对象,转换成较少参数的新的可调用对象。如上面的例子 2能实现重排参数顺序 例: sort(words.begin(),words.end(),bind(ishorter,_2,_1)); //实现单词长度由长到短排列 注意,_1,_2,...等名字在命名空间placeholders中。假如要使用_1,要在程序中加入using std::placeholders::_1 绑定引用参数 有些对象不能拷贝,可以用cref函数。ref(对象)返回一个新对象,包含原对象的引用。新对象是可以拷贝的。 cref(对象)生成一个保存const引用的类。 插入迭代器 有三种: back_inserter(c) //创建一个使用push_back的迭代器(c是支持push_back的容器) front_inserter(c) //创建一个使用push_front的迭代器(c是支持push_front的容器) inserter(c,p) //p是迭代器,创建一个使用insert的迭代器,将元素插入到给定迭代器所表示的元素之前 工作过程: 若it是inserter生成的迭代器,则*it=val;等效于 it=c.insert(it,val); //it指向新加入的元素 ++it; //递增it,使其指向原来的元素 若使用front_inserter. list list copy(lst.cbegin(),lst.cend(),front_inserter(lst2)); //拷贝完成后,lst2包含4 3 2 1 copy(lst.cbegin(),lst.cend(),inserter(lst3,lst3.begin())); //拷贝完成后,lst3包含1 2 3 4 iostream迭代器 istream_iterator 例: ostream_iterator 例 ostream_iterator for(auto e:vec){ *out++=e; //赋值语句实际上将元素写到cout } cout< 可以为任何定义了输入运算符的类型创建istream_iterator对象。可以为任何定义了输出运算符的类型创建ostream_iterator对象 反向迭代器
{
int m = 0, n = 0;
//
不加mutable会报错
//[=] (int a){ m = ++n + a; }(4);
//[m,n] (int a){ m = ++n + a; }(4);
int sum=[=] (int a) { return m = ++n + a; }(4);
//
// [=] (int m, int n, int a){m=++n+a; }(m, n, 4);
// 下面这个函数m,n的值依然会被修改,因为m,n是按引用传入的
// [=] (int &m, int &n, int a){m=++n+a; }(m, n, 4);
cout<
return 0;
}
istream_iterator
istream_iterator
cout<
ostream_iterator
ostream_iterator