目录
一、适配器
定义
适配器是标准库中的一个通用概念。容器、迭代器和函数都有适配器。本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样。
改变函数对象接口的称为 function adapter,改变容器接口的称为 container adapter,改变迭代器接口的称为 iterator adapter。
二、容器适配器
STL定义了3个顺序容器适配器:stack、queue和priority_queue。有关容器适配器的内容请参看STL之顺序容器。
三、函数适配器
3.1、bind()
bind()接受一个可调用对象,生成一个新的可调用对象来"适应"原对象的参数列表。
调用bind的一般形式为:auto newCallable = bind(callable,arg_list);
其中,newCallable 本身是一个可调用对象,arg_list 是一个逗号分隔的参数列表,对应给定的 callable 的参数。即,当我们调用 newCallable 时,newCallable 会调用 callable,并传递给它 arg_list 中的参数。
arg_list 中的参数可能包含形如 _n 的名字,其中 n 是一个整数。这些参数是“占位符”,表示 newCallable 的参数,它们占据了传递给 newCallable 的参数的“位置”。数值 n 表示生成的可调用对象中参数的位置:_1 为 newCallable 的第一个参数,_2 为第二个参数,依此类推。
3.1.1、示例
例如: auto f2 = bind(f1,a,b,_2,_1);
这个bind调用会将 f2(_1,_2) 映射为 f1(a,b,_2,_1)。即对 f2 的调用会调用 f1,用f2的参数代替占位符,再加上绑定的参数a、b。
#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;
void fun1(string &str, int &i)
{
cout << str << ' ' << i << endl;
}
auto fun2 = bind(fun1, _1, _2);
int main()
{
string str("hello");
int i = 0;
fun2(str, i); //输出为:hello 0
return 0;
}
3.1.2、重载函数
为了绑定重载函数的参数,必须显式说明绑定的是哪个版本的函数。
3.1.3、绑定引用参数
默认情况下,bind 的那些不是占位符的参数被拷贝到 bind 返回的可调用对象中。如果希望传递给 bind 一个对象而又不拷贝它,例如当参数为一个ostream,就必须使用标准库的 ref 函数。函数 ref 返回一个对象,包含给定的引用,此对象是可以拷贝的。标准库中还有一个 cref 函数,生成一个保存const引用的类。
使用方式: auto f2 = bind(f1,ref(os),_1);
3.2、mem_fn()
3.2.1、类的成员函数指针
对于类的成员函数指针,若想通过一个指向成员函数的指针进行函数调用,必须首先利用.* 运算符或 ->*运算符将该指针绑定到特定的对象上。因此与普通的函数指针不同,成员指针不是一个可调用对象,这样的指针不支持函数调用运算符。
因为成员指针不是可调用对象,所以我们不能直接将一个指向成员函数的指针传递给算法。例如,如果我们想在一个 string 的 vector 中找到第一个空 string,不能使用下面的语句:
auto fp = &string::empty;//fp指向string的empty函数
find_if(svec.begin(), svec.end(), fp);//错误,必须使用.*或->*调用成员指针
find_if 算法需要一个可调用对象,但我们提供给它的是一个指向成员函数的指针fp。因此在 find_if 的内部将执行如下形式的代码,从而导致无法通过编译:
if(fp(*it)) //错误:要想通过成员指针调用函数,必须使用->*运算符
显然该语句试图调用的是传入的对象,而非函数。
3.2.2、使用mem_fn生成一个可调用对象
mem_fn(mf) 生成一个函数对象,可以作为非成员函数调用。mem_fn()的主要用途是服务与需要非成员函数的算法。通过使用 mem_fn 可以从成员指针生成一个可调用对象,mem_fn 可以根据成员指针的类型推断可调用对象的类型,而无须用户显式地指定:
find_if(svec.begin(), svec.end(), mem_fn(&string::empty));
3.2.3、示例
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<string> vec{"hello", "", "world"};
auto it = find_if(vec.begin(), vec.end(), mem_fn(&string::empty));
*it = "my";
for (auto &str : vec)
cout << str << ' ';//输出为:hello my world
return 0;
}
3.3、not1()和not2()
not1()和not2()用于对返回值进行逻辑否定,别用于一元和二元否定。
3.3.1、not1()
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
struct IntGreaterThanThree
: public std::unary_function<int, bool>
{
bool operator()(int x) const { return x > 3; }
};
int main()
{
vector<int> vec{1, 2, 3, 4, 5};
cout << count_if(vec.begin(), vec.end(), IntGreaterThanThree()) << endl;//2
cout << count_if(vec.begin(), vec.end(), not1(IntGreaterThanThree()));//3
return 0;
}
3.3.2、not2()
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
struct mysort
: public std::binary_function<int, int, bool>
{
bool operator()(int x, int y) const { return x > y; }
};
int main()
{
vector<int> vec{1, 5, 3, 7, 2, 6};
sort(vec.begin(), vec.end(), mysort());
for (auto &i : vec)
cout << i << ' ';//输出为:7 6 5 3 2 1
cout << endl;
sort(vec.begin(), vec.end(), not2(mysort()));
for (auto &i : vec)
cout << i << ' ';//输出为:1 2 3 5 6 7
return 0;
}
3.4、ptr_fun()
将函数指针转换为函数对象。相比于函数指针,函数对象的优势在于,标准库中的对象声明了嵌套的 typedef,它们以统一的名称描述其参数和结果类型。有时候这些typedef是必需的,而不是可选的。
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
int inc(int &i)
{
i++;
return 0;
}
int main()
{
vector<int> vecint{1, 2, 3, 4, 5};
for_each(vecint.begin(), vecint.end(), ptr_fun(inc));
for (auto &i : vecint)
cout << i << ' ';//输出:2 3 4 5 6
return 0;
}
四、迭代器适配器
4.1、插入迭代器
插入迭代器接受一个容器作为参数,生成一个迭代器,能实现向给定容器添加元素。当通过一个插入迭代器进行赋值时,该迭代器调用容器操作来向给定容器的指定位置插入一个元素。
共有3种插入迭代器:
- back_inserter创建一个使用push_back的迭代器
- front_inserter创建一个使用push_front的迭代器
- inserter创建一个使用insert的迭代器。此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前。
#include <iostream>
#include <functional>
#include <list>
#include <algorithm>
using namespace std;
int main()
{
list<int> listint{1, 2, 3};
list<int> listdes;
copy(listint.begin(), listint.end(), back_inserter(listdes));
for (auto &i : listdes)
cout << i << ' '; //输出:1 2 3
auto it = front_inserter(listdes);
*it = 4;
for (auto &i : listdes)
cout << i << ' '; //输出:4 1 2 3
auto it1 = inserter(listdes, listdes.end());
*it1 = 5;
for (auto &i : listdes)
cout << i << ' '; //输出:4 1 2 3 5
return 0;
}
4.2、反向迭代器
反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。对于反向迭代器,递增(以及递减)操作的含义会颠倒过来。递增一个反向迭代器(++it)会移动到前一个元素;递减一个迭代器(–it)会移动到下一个元素。
#include <iostream>
#include <functional>
#include <list>
#include <algorithm>
using namespace std;
int main()
{
list<int> listint{1, 2, 3};
for (auto it = listint.rbegin(); it != listint.rend(); it++)
cout << *it << ' '; //输出:3 2 1
return 0;
}
4.2.1、反向迭代器和其他迭代器之间的关系
对于序列[b:e),反向迭代器从序列尾部向序列起始位置进行遍历。为了获得一个半开区间,必须将b - 1视为序列的尾后位置,将e - 1视为起始位置,从而得到半开区间
[e - 1, b - 1)。因此,一个反向迭代器与其底层迭代器之间的根本关系是:
&*(reverse_iterator§)==&*(p - 1)。例如,如果vec为一个vector,则vec.rbegin()指向其尾元素vec[vec.size() - 1]。