C++标准库定义了一组泛型算法实现了一些经典算法的公共接口 如排序 搜索等 它们可以适用于不同类型的元素和多种容器类型
这些算法大多定义在algorithm头文件中 并在numeric头文件中定义了一组数值算法
一般情况下这些算法并不直接操作容器 而是通过迭代器来遍历范围 处理元素
泛型算法永远不会改变底层容器的大小 只会运行在迭代器上 执行迭代器的操作
泛型算法的结构类似 除了少数例外 都对一个范围内的元素进行操作 通过两个迭代器来确定输入范围
接受输入范围的算法总是使用前两个参数来表示此范围
Find(vec.begin(),vec.end(), 'a'); //查找 若未找到 返回尾后迭代器
accumulate(vec.begin(),vec.end(), 0); // 求和 和的初值为0
accumulate(vec.begin(),vec.end(), string(""));
//求和 初值为空串 注意不可直接用字面值空串 其类型为const char* 无对应+操作
注意第三个参数和初值必须与求和的类型相容 也决定了最终返回类型
比较两个序列中元素是否相同
Equal(vec1.cbegin(),vec1.cend(), vec2.cbegin()); // 序列2至少和序列1一样长
接受谓词的算法
谓词是一个可调用的表达式 其结果是一个能用作条件的值
标准库算法所使用的谓词分为两类
- 一元谓词(只接受单一参数)
- 二元谓词(两个参数)
接受谓词参数的算法会对输入序列中元素调用谓词 因此这些元素必须能转换成谓词的参数类型
Bool isshorter(const string&s1, const string&s2){
return s1.size() < s2.size();
}
Sort(vec.begin(),vec.end(), isshorter);
注意 这个sort传入的比较函数 以你返回true的类型排序
即想升序排序 就使小于时为true 反之 大于时为true
lambda
表达式
一个lambda
表达式表示一个可调用的代码单元
可以理解为是一个未命名的内联函数 本质上是函数对象
与函数的不同在于
- lambda表达式可以定义在函数内部
- 不能使用默认参数
- 且必须使用尾置返回来指定返回类型
其形式为
[capture list] (parameter list) -> return type {function body}
参数列表和返回类型为可选项们,可以忽略
若忽略返回类型
如果函数只包含单一一条return语句 返回return语句对应类型
否则返回void
auto f = [] {return 42;};
cout<<f()<<endl;
通过捕获列表 lambda
表达式可以使用除了参数以外的值
int sz=5;
auto wc = find_if(vec.begin(),vec.end(), [sz](const string &a){return a.size()>= sz;});
//返回第一个长度不小于sz的元素
当定义一个lambda
时 编译器生成一个与lambda
对应的未命名的类类型
默认情况下 从lambda生成的类都包含对应该lambda所捕获的变量的数据成员 并在其对象创建时被初始化
捕获列表默认是值传递 也可以使用引用传递
但以引用方式捕获一个变量时 必须保证在lambda
执行时变量存在
因此 尽可能避免捕获引用或指针以防止潜在的问题
int v1 = 4;
auto f = [&v1] {return v1;};
v1=0;
cout<<f()<<endl; //结果为 0 因为传递的是引用 实际使用的是v1的值
捕获列表中 =
表示值传递 &
表示引用传递
lambda
可以通过隐式方式自动捕获所需变量 即只指出捕获方式 当函数内使用时会自动捕获外部变量
[&](){ sz = 1;} // 自动以引用传递方式捕获到变量sz
[=](){ int a = sz;} // 自动以值传递方式捕获到变量sz
同时也可以通过混用显式和隐式方式来对某一部分采用值传递捕获 另一部分采用默认方式隐式捕获
[=, reference_list](){} //reference list里的变量按引用捕获,其余的变量按值捕获,比如 [=, &a, &b] 表示 a 和 b 按引用,其余按值捕获
[&, identifier_list]( ){ } // identifier list里的变量按值捕获,其余的变量按引用捕获,比如 [&, a, b] 表示 a 和 b 按值,其余按引用捕获
其他标准库算法
for_each()
接受一个可调用对象 并对输入序列中的每个元素调用此对象
for_each(vec.begin(), vec.end(), [](const string &a){cout<<a<<endl;});
通过bind()
函数可以接受一个可调用对象 生成一个新的可调用对象来适应原对象的参数列表
bind
定义在functional
头文件中
通过形如_n
的占位符来表示传递给新对象的参数 n
表示参数的位置
bool check(const string &a, int sz){
return a.size()>= sz;
}
auto check2= bind(check, _1, 6);
String s= "hi";
check2(s);
更一般地 还可以通过bind
设置调用对象参数 或重新安排参数顺序
假设f
是一个具有5个参数的函数
auto g= bind(f,a,b,_1,c,_2);
//即g为一个两个参数的函数 其第一个参数输入到f的第3个参数
//第二个参数输入到f的第5个参数 f的1 2 4参数分别绑定为a,b,c
bind
是通过拷贝来传递参数
若无法拷贝(如IO类)或需使用引用时 必须通过ref()
函数返回对应对象的引用
同样该函数也定义在functional
头文件中
特殊迭代器
插入迭代器是一种向容器中添加元素的迭代器 如back_inserter
对该迭代器的赋值会调用push_back将一个具有该值的元素添加到容器中
back_inserter()
front_inserter()
分别插入元素到头尾
inserter()
接受第二个参数 将元素插入到给定的迭代器之前
流迭代器可以从流对象中读取数据
istream_iterator<int> int_in(cin), eof;
同样 可以使用泛型算法操作流迭代器
cout<< accumulate(int_in, eof, 0) <<endl;
输出流迭代器的赋值语句事实上将值写入到流中
使用反向迭代器搜索后需要通过.base()
转换成正向 否则内容也会反向处理
如 "last" 会变为 "tsal"