泛型算法

来源:互联网 发布:note4手机备份软件 编辑:程序博客网 时间:2024/06/07 16:34

插入迭代器 back_inserter:
back_inserter接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器。当我们通过此迭代器赋值时,赋值运算符会调用push_back将一个具有给定的元素添加到容器中。定义在头文件iterator中。

vector<int> vec;auto it=back_inserter(vec);//返回一个插入迭代器*it=42;//赋值调用push_back

向算法传递函数:

我们可以向泛型算法传递自定义的函数(谓词)用来定义我们想要的排序方式。谓词是一个可调用的表达式,其返回结果是一个能用作条件的值。接受谓词参数的算法对输入序列的元素调用谓词。元素类型必须能转换为谓词的参数类型。

bool isShorter(const string &s1,const string &s2){    return s1.size()<s2.size();}sort(words.begin(),words.end(),isShorter());

lambda表达式:

一个lambda表达式表示一个可调用的代码单元。可以理解为一个未命名的内联函数。一个lambda具有一个返回类型,一个参数列表和一个函数体。形式如下,其中 capture list是一个lambda所在函数中定义的局部变量的列表。lambda必须使用尾置返回来指定返回类型。

[capture list](parameter list)-> return type { function body }

我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体。

auto f= [] { return 42; }

向lambda传递参数:

lambda不能有默认参数,一个lambda调用的实参数目永远与形参数目相等。一个lambda实例:

[](const string &a,const string &b) { return a.size() < b.size(); }

使用捕获列表:

一个lambda只有在其捕获列表中捕获一个它所在函数中的局部变量,才能在函数体中使用该变量。

[sz] (const string &a) { return a.size() >= sz; }

在泛型算法使用lambda:

//获取一个迭代器,指向第一个满足size()>=sz的元素auto wc=find_if(words.begin(),words.end(),        [sz] (const string &a) { return a.size() >= sz; });

lambda 捕获和返回:

值捕获:

采用值捕获的前提是变量可以拷贝。与参数不同,被捕获的变量的值是在lambda**创建时被拷贝,而不是调用时拷贝**。

void fcn(){    size_t v1=42;    auto f= [v1] { return v1; };    v1=0;    auto j=f();//j=42;}

引用捕获:

采用引用方式捕获一个变量,就必须确保被引用对象在lambda执行的时候是存在的。lambda捕获的都是局部变量,这些变量在函数结束后就不复存在了。

void fcn(){    size_t v1=42;    auto f= [&v1] { return v1; };    v1=0;    auto j=f();//j=0;}

隐式捕获:

我们可以使用隐式捕获让编译器根据lambda体中的代码来对推断我们要使用那些变量。&表示编译器采用捕获引用方式,=则表示采用值捕获方式。

//sz为隐式捕获,值捕获方式wc=find_if(words.begin(),words.end(),            [=] (const string &s)){ return s.size()>=sz; };

如果我们希望对一部分变量采用值捕获,对其他变量采用引用捕获,可以混合使用隐式捕获和显式捕获。

//os隐式捕获,引用捕获方式;c显式捕获,值捕获方式for_each(words.begin(),words.end(),            [&,c] (const string &s) { os<<s<<c; });

当我们混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个&或=。此符号指定了默认捕获的方式为引用或值。另外,显示捕获的变量必须使用与隐式捕获不同的方式。

这里写图片描述

可变lambda:

默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果我们希望能改变一个被捕获的变量的值,就必须在参数列表上加上关键字mutable.

void fcn3(){    size_t v1=42;    auto f= [v1] mutable { return ++v1; };    v1=0;    auto j=f();//j=43;}

一个引用捕获的变量是否可以修改依赖于此引用指向的是一个const类型还是非const类型:

void fcn4(){    size_t v1=42;    auto f= [&v1] { return ++v1; };    v1=0;    auto j=f();//j=1;}

指定lambda返回类型:

默认情况下,如果一个lambda包含return之外的任何语句,则编译器假定此lambda返回void. 则被推断返回void的lambda不能返回值。

当我们需要为一个lambda定义返回类型时,必须使用尾置返回类型。

transform(vi.begin(),vi.end(),vi.begin(),            [](int i)->int             { if (i<0) return -i;else return i;});

标准库bind函数:

定义在头文件functional中,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

调用bind的一般形式为:

auto newCallable = bind(callable, arg_list);

当我们调用newCallable时,newCallable会调用callable,并传递给它arg_list中的参数。arg_list中的参数可能包含形如_n的名字,n是一个整数。这些参数是“占位符”,表示newCallable的参数,他们占据了传递给newCallable的参数的“位置”。

auto check=bind(check_size,_1,6);string s="hello";bool b1=check6(s);//check6(s)会调用check_size(s,6)

使用placeholders名字:

名字_n都定义在一个名为placeholders的命名空间中,而这个命名空间本身定义在std命名空间。我可以使用using namespace namespace_name这种形式说明希望所有来自namespace_name的名字都可以在我们的程序中使用。

bind的参数:

我们可以用bind绑定给定可调用对象的参数或重新安排其顺序。

//假定f是一个有5个参数可调用对象auto g=bind(f,a,b,_2,c,_1);调用g(X,Y)会调用f(a,b,Y,c,X);

默认情况下,bind的那些不是占位符的参数被拷贝到bind返回的可调用对象中。如果我们希望传递给bind一个对象而又不拷贝它,就必须使用标准库ref函数,定义在头文件functional中。

for_each(words.begin(),words.end(),bind(print,ref(os),_1,' '));

插入迭代器:

back_inserter : 创建一个使用push_back的迭代器。

front_inserter : 创建一个使用push_front的迭代器。

inserter : 创建一个使用insert的迭代器。此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。元素被插入到给定迭代器所表示的元素之前。

inserter 插入器的工作原理:

当调用inserter (c, iter )时,我们得到一个迭代器,使用它会将元素插入到iter原来指向的元素之前的位置。即,如果it是inserter生成的迭代器,则

*it=val;等价于it=c.insert(it,val);++it;

当我们使用front_inserter时,元素总是插入到容器第一个元素之前。

list<int> lst={1,2,3,4};list<int> lst2,lst3;copy(lst.cbegin(),lst.cend(),front_inserter(lst2));//lst={4,3,2,1}copy(lst.cbegin(),lst.cend(),inserter(lst3,ls3.begin));//lst3={1,2,3,4}

iostream迭代器:

istream_iterator: 读取输入流

ostream_iterator: 向一个输出流写数据。

istream_iterator<int> in_iter(cin);//从cin读取intistream_iterator<int> eof;//默认初始化迭代器,创建一个istream尾后迭代器while(in_iter!=eof)    vec.push_back(*in_iter++);

istream_iterator操作:

这里写图片描述

ostream_iterator操作:

这里写图片描述

ostream_iterator<int> out_iter(cout," ");for(auto e:vec)    *out_iter++=e;//or out_iter=e;cout<<endl;

反向迭代器:

反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。++it会移动到前一个元素,–it会移动到下一个元素。可以调用rbegin, rend,crbegin,crend成员函数来获得反向迭代器。

这里写图片描述

反向迭代器会反向处理string.故如果我们现在得到一个string的正常顺序,需要把迭代器通过base()函数转换成对应的普通迭代器。

auto comma=find(line.cbegin(),line.cend(),',');cout<<string(line.cbegin(),comma)<<endl;auto rcomma=find(line.crbegin().line.crend,',');cout<<string(line.crbegin().rcomma)<<endl;cout<<string(rcomma.base().line.cend())<<endl;

这里写图片描述


算法形参模式:

alg(beg,end,other args);alg(beg,end,dest,other args);alg(beg,end,beg2,other args);alg(beg,end,beg2,end2,other args);

其中alg是算法的名字,beg和end表示算法所操作的输入范围。

dest参数是表示一个算法可以写入的目的位置的迭代器。向输出迭代器写入数据的算法都假定目标空间足够容纳写入的数据。

接受单独的beg2或者接受beg2和end2的算法用这些迭代器表示第二个输入范围。这些算法通常使用第二个范围的元素与第一个输入范围结合来进行一些运算。

算法命名规范:

接受一个元素值得算法通常有另一个不同名的版本,该版本接受一个谓词代替元素值。接受谓词参数的算法都附加的_if前缀。

find(beg,end,val);//查找输入范围内中val第一次出现的位置find_if(beg,end,pred);//查找第一个令pred为真的元素

区分拷贝元素的版本和不拷贝的版本:

默认情况下,重排元素的算法将重排后的元素写回给定的输入序列中。这些算法还提供另一个版本。将元素写到一个指定的输出目的位置。只需在算法名字附加_copy.

reverse(beg,end);//反转输入范围中的元素顺序reverse_copy(beg,end,dest);//将元素按逆序拷贝到dest
0 0
原创粉丝点击