什么是STL?

STL是“Standard Template Library”,简单来说就是标准工具库,是C++编译器提供的一套易用的现成的算法工具。

优点

如何OI选手能够合理运用STL,可以节省大量代码量,快速切题。 实际上,Ol选手常说的STL除了指真正属于STL中的函数以外,还包含一些易用的库函数。由于STL内容实在不多,在这里我们讲一点其他常用函数、使用技巧。都是用法、不太动脑子,内容不多,所以我可能会插两道要用STL的题让大家想一想。

STL六大部件

STL主要包含容器(containers)、迭代器(iterators)、空间配置器(allocator)、配接器(adapters,或叫适配器)、算法(algorithms)、仿函数(functors,或叫函数对象)等六大部件,相互关连构成一个整体: 数组也是一个容器,但它的缺陷就是类型不是泛型,不能动态增长容量,STL的容器就是用来解决此类问题。通过模板来支持泛型,通过allocator(也是模板)来支持动态增长容量,通过迭代器(用类封装指针的模板类,实现了诸如运算符:*、->、++、--、+n、-n的重载来实现指针的移动)来容器和算法,由此实现算法作用在容器上的泛化。

以下是一个有使用到STL六大部件的小实例:

vector<int,allocator > vi(ia,ia+6);

vector是一种容器类型。

int是容器内元素的类型。

allocator是指定的分配器类型,其也是一种模板,需要指定类型,这里指定int。容器构造函数有提供默认的分配器,所以也可以写为:

vector vi(ia,ia+6);

模板类的构造函数有重载,可以使用数组的元素做为vector容器的元素。

适配器在STL中扮演着转换器的角色,本质上是一种设计模式,用于将一种接口转换成另一种接口(或者说连接不同的接口),从而是原本不兼容的接口能够很好地一起运作。比如我们有一个接口 void fun(CString a) , 只接受字符串类型变量, 但是提供的数据又只有一个整型变量int a, 这时就需要一个适配器来把 int a 转换 CString a;

适配器不提供迭代器。

根据目标接口的类型,适配器可分为以下几类:

(1) 改变容器的接口,称为容器适配器; (2) 改变迭代器的接口,称为迭代器适配器; (3) 改变仿函数的接口,称为仿函数适配器。

STL提供了序列式容器,同时针对序列式容器提供了应用于不同场景的容器适配器,通俗讲容器适配器就是以序列式容器为底层数据结构,进一步封装了的为适应场景应用的容器。STL中提供了三种适配器,分别为stack,queue和priority_queue。

函数对象的适配器,用于特化和扩展一元或者二元函数对象,主要分为两类:

绑定器(binder):给二元函数对象绑定一个常量,转化成一元函数对象。包括bind1st和bind2nd。

取反器(negator):将谓词函数对象结果取反。包括not1和not2。

对于迭代器适配器,如输入流和输出流都不支持迭代器,而STL算法都是通过操作迭代器来实现的,如果需要用STL算法处理流中输入的数据时,就用std::istream_iterator,这就是一个适配器,它提供输入迭代器的接口,使用istream来实现。迭代器适配器包括

三种reverse(逆向)适配器,如rbegin(),rend()等。

insert迭代器,带有一个容器参数,有back_inserter(容器)、front_inserter(容器)、inserter(容器,位置)。

stream适配器:如ostream_iterator,istream_iterator。

bind1st和bind2nd函数用于将一个二元函数对象(binary functor,bf)转换成一元函数对象(unary functor,uf)。为了达到这个目的,它们需要两个参数:要转换的bf和一个值(v)。

我们在做比较的时候所写的表达式像 x > k ,x < k,这里的k是一个参数,表示你程序里面的表达式要和k值去比较。bind2nd简单的理解就是把k作为比较表达式的第二个参数。如果使用bind1st则对应的表达式是 k > x,k < x,也就是把k作为比较表达式的第一个参数。

f = std::bind1st( functor, v); 'f( x)'等价于'functor( v, x)'

f = std::bind2nd( functor, v); 'f( x)'等价于'functor( x, v)'

bind1st和bind2nd例子:

int a[] = {1, 2, 100, 200}; std::vector< int> arr(a, a + 4); // 移除所有小于100的元素 arr.erase( std::remove_if( arr.begin(), arr.end(),std::bind2nd( std::less< int>(), 100)), arr.end()); 这里的比较表达式相当于arr.value < 100。

如果用bind1st则表达的意思就恰恰相反:

// 移除所有大于100的元素 arr.erase( std::remove_if( arr.begin(), arr.end(),std::bind1st( std::less< int>(), 100)), arr.end()); 这里的表达式相当于100 < arr.value。

为了实现删除大于100的元素你同样可以使用bind2nd:

// 移除所有大于100的元素 arr.erase( std::remove_if( arr.begin(), arr.end(),std::bind2nd( std::greater< int>(), 100)), arr.end()); x <= k怎么实现呢,std又提供了一个好东西not1,我们可以说 !(x > k) 和 x <= k是等价的,那么我们看看下面的表达式:

// 移除所有小于等于100的元素 arr.erase( std::remove_if( arr.begin(), arr.end(),std::not1(std::bind2nd( std::greater< int>(), 100))), arr.end()); not1是构造一个与谓词结果相反的一元函数对象,not2是构造一个与谓词结果相反的二元函数对象。 int a[] = {1, 2, 100, 200};

vector< int> arr(a, a + 4);

// 移除所有小于100的元素

arr.erase( std::remove_if( arr.begin(), arr.end(),

bind2nd( std::less< int>(), 100)), arr.end());

//这里的比较表达式相当于arr.value < 100

//如果用bind1st则表达的意思就恰恰相反

// 移除所有大于100的元素

arr.erase( std::remove_if( arr.begin(), arr.end(),

bind1st( std::less< int>(), 100)), arr.end());

//这里的表达式相当于100 < arr.value

//当然为了实现删除大于100的元素你同样可以使用bind2nd

// 移除所有大于100的元素

arr.erase( std::remove_if( arr.begin(), arr.end(),

bind2nd( std::greater< int>(), 100)), arr.end());

//前面说道=的比较,比如说x <= k怎么实现呢,std又提供了一个好东西not1,

//我们可以说 !(x > k) 和 x <= k是等价的,那么我们看看下面的表达式:

// 移除所有小于等于100的元素

arr.erase( std::remove_if( arr.begin(), arr.end(),

not1(std::bind2nd( std::greater< int>(), 100))), arr.end());

//说明:not1是否定返回值是单目的函数,std中还有not2它是否定返回值是双目的函数。```cpp int a[] = {1, 2, 100, 200};

vector< int> arr(a, a + 4);

// 移除所有小于100的元素

arr.erase( std::remove_if( arr.begin(), arr.end(),

bind2nd( std::less< int>(), 100)), arr.end());

//这里的比较表达式相当于arr.value < 100

//如果用bind1st则表达的意思就恰恰相反

// 移除所有大于100的元素

arr.erase( std::remove_if( arr.begin(), arr.end(),

bind1st( std::less< int>(), 100)), arr.end());

//这里的表达式相当于100 < arr.value

//当然为了实现删除大于100的元素你同样可以使用bind2nd

// 移除所有大于100的元素

arr.erase( std::remove_if( arr.begin(), arr.end(),

bind2nd( std::greater< int>(), 100)), arr.end());

//前面说道=的比较,比如说x <= k怎么实现呢,std又提供了一个好东西not1,

//我们可以说 !(x > k) 和 x <= k是等价的,那么我们看看下面的表达式:

// 移除所有小于等于100的元素

arr.erase( std::remove_if( arr.begin(), arr.end(),

not1(std::bind2nd( std::greater< int>(), 100))), arr.end());

//说明:not1是否定返回值是单目的函数,std中还有not2它是否定返回值是双目的函数。

not2 example:

```cpp
 #include <bits/stdc++.h>  
using namespace std;

int main () {
 	int foo[] = {10,20,30,40,50};
 	int bar[] = {0,15,30,45,60};
	pair<int*,int*> firstmatch,firstmismatch;
 	firstmismatch =  mismatch (foo, foo+5, bar, equal_to<int>());
 	firstmatch =  mismatch (foo, foo+5, bar,  not2(equal_to<int>()));
	cout << "First mismatch in bar is " << *firstmismatch.second << '\n';
	cout << "First match in bar is " << *firstmatch.second << '\n';
 	return 0;
}
/* 输出结果: 
First mismatch in bar is 0
First match in bar is 30
*/

附小实例代码:

/*#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
*/
#include <bits/stdc++.h>
using namespace std;

int main()
{
	int ia[7] = {26,210,12,47,109,83};
	vector<int,allocator<int> > vi(ia,ia+6);	// 分配器也是一个模板,需要告诉它每次分配的是什么东西
	cout<<count_if(vi.begin(), vi.end(), not1(bind2nd(less<int>(),40)));// 统计小于40的否定的元素数量
	return 0;
}