}
当定义一个lambda时,编译器会生成以个与lambda对应的新的(未命名)类类型。
当使用lambda初始化的变量时,定义一个从lambda生成的对象。
默认情况,从lambda生成的类包含一个对应这个lambda所捕获的变量的数据成员,类似普通成员,lambda的数据成员也在lambda对象被创建时被初始化
捕获变量的值在lambda创建时拷贝,因而随后对变量的修改不会影响到对应lambda内对应的值
autof2=[&v1]{returnv1;};//返回的是v1指向的对象的值
io对象不可拷贝,所以对于io对象来说,我们就可以用引用来传递了。
特别的,对于捕获引用来说,必须保证引用是存在的,且有预期的值
一般来说,我们应该尽量减少捕获的数量,来避免潜在的捕获导致的问题,而且,如果可能的话,应该避免捕获指针或引用
隐式捕获,除了显示的列出希望使用的来自所在函数的变量之外,我们也可以让编辑器根据lambda中的代码来推测,
&捕获引用;=值捕获
//sz采用隐式捕获,值捕获方式
wc=find_if(words.begin(),words.end(),[=](conststring&s){returns.size()>=sz;);
如果我们希望对一部分变量采用值捕获,对其他变量采用引用捕获,可以混合使用隐式捕获和显示捕获
for_each(words.begin(),words.end(),[&,c](conststring&s){os<
当我们混合使用了隐式捕获和显示捕获时,捕获列表中的第一个元素必须是一个&或=。
此符号指定了默认的捕获方式
但使用混合捕获方式时,显示与隐式必须采用不同的捕获方式。
如,如果隐式捕获采用引用方式,则显式捕获命名变量必须采用值得方式
默认情况下,对于一个被拷贝的变量,lambda不会改变其值,若果要改变被捕获的值,就必须在参数列表首加上mutable,因此可以省略参数列表
autof=[v1]()mutable{return++v1;};
一个引用捕获的变量是否可以修改依赖于此引用指向的是一个const还是非const类型
autof2=[&v1]{return++v1;};
默认情况下,如果一个lambda体包含return之外的任何语句,则编译器假定返回void。
就是没有返回值、
transform(vi.begin(),vi.begin(),vi.begin(),[](inti){returni<0?
-i:
i;});//将序列中的每一个负数替换成绝对值。
前两个迭代器是输入范围,第三个迭代器是目的位置,我们无须指定返回类型,可以根据条件运算符的类型推断出来
transform(vi.begin(),vi.end(),vi.begin(),[](inti){if(i<0)return-i;elsereturni;});//错误
编辑器推断这个版本的lambda返回类型为void,但返回一个int,此时我们就好指定返回类型
transform(vi.begin(),vi.end(),vi.begin(),[](inti)->int{if(i<0)return-i;elsereturni;});
对于那种只要一两个地方使用的简单操作,lambda表达式是最有用的,若果在多个地方,或这比较复杂,通常用的是函数
如果lambda表达式的捕获列表是空的,通常可以用函数来代替
定义在functional头文件中的bind函数:
可以看做是一个通用的函数适配器,它接受一个可调用的对象,生成一个新的可调用对象“适应”源对象的参数列表,autonewCallable=bind(callable,arg_list)
当我们调用newCallable时,newCallable会调用callable,并传递它arg_list中的参数
arg_list中的参数可能使_n.n是整数。
这些参数是占位符,数值n表示的是生成可调用对象的位置:
_1为newCallable的第一个参数,依次类推
autowc=find_if(words.bengin(),words,end(),bind(check_size,_1,sz);
_n定义在placeholders的命名空间中,而这个命名空间本身定义在std命名空间中。
_1的声明为 usingstd:
:
placeholders:
:
_1;
与bind函数一样,palceholders命名空间也定义在functional头文件中
用bind重排参数顺序
sort(words.begin(),words.end(),isShorter);///按单词长度由短到长排序
sort(words.begin(),words.end(),bind(isShorter,_2,_1));///由长到短
默认情况下,bind的那些不是占位符的参数被拷贝到bind返回的可调用对象中,但是我们可以使用引用
for_each(words.begin(),words.end(),[&os,c](conststring&s){os<
ostream&print(ostream&os,conststring&s,charc)
{
returnos<
}
for_each(words.begin(),words.end(),bind(print,os,_1,''));//错,不能拷贝os
for_each(words.begin(),words.end(),bind(print,ref(os),_1,''));
ref返回的一个对象,包含给定的引用,此对象可以拷贝。
标准库中还有一个cref函数,生成的是const引用的类。
这两个函数在functional头文件中(老版c++用的是bind1st和bind2nd,已经被弃用)
除了为每个容器定义的迭代器之外,标准库的iterator头文件中还定义了额外的几种迭代器
插入迭代器:
这些迭代器被绑定到一个容器上,可用来向容器插入元素
流迭代器:
这些迭代器被绑定到输入或输出流上,可用来遍历所关联额IO流
反向迭代器:
这些迭代器向后而不是向前移动,除了forward_list之外的标准库容器都有反向迭代器。
移动迭代器:
这些专用的迭代器不是拷贝其中的元素,而是移动它们
插入迭代器是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向给定容器添加元素。
it=t; 在it指定的当前位置插入t。
假定c是it绑定的容器,依赖于插入迭代器的不同种类,此赋值会分别调用c.push_back(t) c.push_front(t) c.insert(t,p),其中p为传递给inserter的迭代器位置
*it ,++it,it++ 这些操作虽然存在,但不会对it做任何事情。
每个操作都返回it
插入迭代器有三种,差异在于元素的插入位置
back_inserter 创建一个使用push_back的迭代器
front_inserter 创建一个使用push_front的迭代器
insert 创建一个使用insert的迭代器。
此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器,元素被插入到给定迭代器所表示的元素之前
只有在容器支持对应操作的情况下,我们才能使用对应的迭代器
调用insert(c,iter)时,得到一个迭代器,接下来使用它会将元素插入iter原来所指向的元素之前的位置。
*it=val;
其效果与下面代码一样:
it=c.insert(it,val);//it指向新插入的元素
++it; //递增it使他指向原来元素
当我们使用front_inserter时,元素总是插入到容器第一个元素之前。
而对于inserter指向第一个元素,只要我们插入新元素,此元素就不是首元素了,当调用front_inserter(c),我们得到一个插入迭代器,接下来会调用push_front。
当每个元素被插入c中时,它变为首元素,可见,front_inserter生成的迭代器会将插入的元素序列顺序颠倒过来,而inserter和back_inserter则不会
istream_iterator 读取输入流,ostream_iterator 向一个输出流写数据
当创建一个流迭代器时,必须指定迭代器将要读写对象的类型,一个istream_iterator使用>>来读取流,因此该类型必须定义了输入允许符。
创建时,我们可以将它绑定到一个流上,也可以默认初始化,来创建一个可以当作尾后值使用的迭代器
istream_iteratorint_it(cin);
istream_iteratorint_eof; //定义成空
while(int_it!
=int_eof) //当有数据可供读取时执行,一旦其关联的流遇到文件尾或遇到IO错误,迭代器的值就与尾后迭代器相同
vec.push_back(*in_iter++); //存入vector
使用一对表示范围的迭代器来构造vector:
istream_iteratorin_iter(cin),eof:
vectorvec(in_iter,eof);
in_iter 返回从流中读取的值
in->men 与(*in).men含义相同
使用算法:
istream_iteratorin(cin),eof:
cout<istream_iterator绑定到一个流时,标准库不保证迭代器立即从流读取数据,保证的是,在我们第一次解引用迭代器之前,从流中读取数据的操作已经完成。
一般情况无所谓,如果我们创建了一个istream_iterator,么有使用就销毁了,或者我们正在从两个不同的对象同步读取同一个流,那么何时读取就很重要了
我们可以对任何带有输出运算符的类型定义ostream_iterator。
创建时我们可以提供第二个参数,一个c风格的字符串。
在输出每个元素后都会打印此字符串。
必须将ostream_iterator绑定到一个流上,不允许空的
out=val; 用<<运算符将val写入到out所绑定的ostream中。
val的类型必须与out可写的类型兼容
*out ++out out++这些运算符值存在的,但是不对out做任何事情,每个运算符都返回out
ostream_iteratorout_iter(cout,"");
for(autoe:
vec)
*out_iter++=e;
cout<值得注意的是,当我们向out_iter赋值时,可以忽略解引用可递增运算。
循环写成
for(autoe:
vec)
out_iter=e;
cout<也可以通过copy类打印:
copy(vec.begin(),vec.end(),out_iter);
cout<除了forward_list之外,其他容器都支持反向迭代器。
我们可以通过调用rbegin、rend、crbegin、crend成员函数来获得反向迭代器。
返回的是指向容器尾元素和首元素之前一个位置的迭代器。
sort(vec.begin(),vec.end());//按正常顺序
sort(vec.rbegin().vec.rend());//逆序
因为流迭代器不支持递减运算,因为不可能在一个流中反向移动。
因此不能砸iforward_list和流迭代器创建反向迭代器
反向迭代器的目的是表示元素的范围,而这些范围是不对称的,导致一个结果:
当我们从一个普通迭代器初始化一个反向迭代器,或是给反向迭代器赋值,结果迭代器与原迭代器指向的不是相同的元素
五类迭代器:
输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器
除了输出迭代器之外,一个高层类别的迭代器支持低层类别迭代器的所有操作
(对于传递错误类别迭代器,很多编译器不会给出任何警告或提示)
find算法在一个序列上进行一遍扫描,对元素进行只读操作,至少需要输入迭代器
replace函数需要一对迭代器,至少是前向迭代器。
replace_copy前两个迭代器参数要去至少是前向迭代器。
其第三个表示目的位置,必须至少是输出迭代器。
输入迭代器:
可以读取序列中的元素。
必须支持:
== !
= ++ * ->
输入迭代器只用于顺序访问,*it++保证是有效的,但递增科恩能够导致所有其他指向流的迭代器失效,因此,输入迭代器只能用于单遍扫描的算法。
find和accumulate
输出迭代器,必须支持:
++ *
只能向输出迭代器赋值一次,而且只能用于单遍扫描的算法。
用作目的位置的迭代器通常就是输出迭代器,如,copy的第三个参数
前向迭代器:
支持输入输出的说有操作,使用前向迭代器可以进行多遍扫描,replace要求的就是前向迭代器,forward_list上的就是
双向迭代器,支持前向迭代器的所有操作,还支持递减运算符(--)reverse要去的就是双向迭代器
随机访问迭代器,sort要求随机访问,array、deque、string、vector的迭代器都是随机访问迭代器
使用谓词的算法
unique(beg,end);
unique(beg,end,comp);
_if版本:
find(beg,end,val)//查找输入范围中val第一次出现的位置
find(beg,end,pred);//查找第一个令pred为真的元素
默认情况,重排算法将重排后的元素写回给定的输入序列中,这些算法还提供了另一个版本,将元素写到一个指定的输出目的位置。