配接器 adapters

  • 1.配接器之概观与分类
  • 2.conrainer adapters
    • 3.iterator adapters
      • 4.function adapters
  • 配接器(adapters)在STL组件的灵活组合运用功能上,扮演着轴承、转换器 的角色。

    • Adapter这个概念,事实上是一种设计模式(design pattern)。

      • 《Design Patterns》一书提到23个最普及的设计模式,其中对adapter样式的定义如下:将一个class的接口转换为另一个class的接口,使原本因接口不兼容而不能合作的 classes, 可以一起运作。

1.配接器之概观与分类

STL所提供的各种配接器中,改变仿函数(functors)接口者,我们称为function adapter, 改变容器(containers)接口者,我们称为container adapter, 改变迭代器(iterators)接口者,我们称为iterator adapter。

1.1 应用于容器,container adapters

STL提供的两个容器queue和stack,其实都只不过是一种配接器。它们修饰deque的接口而成就出另一种容器风貌。这两个container adapters已于第4章介绍过。

1.2 应用于迭代器,iterator adapters

STL提供了许多应用于迭代器身上的配接器,包括insert iterators , reverse iterators, iostream iterators。C++Standard规定它们的接口可以藉由<iterator>获得,SGI STL则将它们实际定义于<stl_iterator.h>。

①.Insert Iterators

所谓insert iterators,可以将一般迭代器的赋值(assign)操作转变为插入(insert)操作。这样的迭代器包括专司尾端插入操作back_insert_iterator,专司头端插入操作的front_insert_iterator,以及可从任意位置执行插人操作 的insert_iterator。由于这三个iterator adapters的使用接口不是十分直观,给一般用户带来困扰,因此,STL更提供三个相应函数:back_inseter()、front_inserter()、inserter(),如下图所示,提升使用时的便利性。

②.Reverse Iterators

所谓reverse iterators , 可以将一般迭代器的行进方向逆转,使原本应该前进的 operator++变成了后退操作,使原本应该后退的operator–变成了前进操作。 这种错乱的行为不是为了掩入耳目或为了欺敌效果,而是因为这种倒转筋脉的性质运 用在“从尾端开始进行”的算法上,有很大的方便性。稍后有一些范例展示。

③.IOStream Iterators

  1. 所谓iostream iterators , 可以将迭代器绑定到某个iostream对象身上。绑定到istream对象(例如std::cin)身上的,称为istream_iterator, 拥有输 入功能;绑定到ostream对象(例如std::cout)身上的,称为 ostream_ iterator, 拥有输出功能。这种迭代器运用于屏幕输出。
  2. 项以它为蓝图,稍加修改,便可适用于任何输出或输入装置上。例如,你可以在透彻了解iostream Iterators的技术后,完成一个绑定到Internet Explorer cache身上的迭代器,或是完成一个系结到磁盘目录上的一个迭代器。
  3. 请注意,不像稍后即将出场的仿函数配接器(functor adapters)总以仿函数作 为参数,予人以“拿某个配接器来修饰某个仿函数”的直观感受,这里所介绍的 迭代器配接器(iterator adapters)很少以迭代器为直接参数(通常它们以容器为直接参数,而每一个容器都有自己专属的迭代器,因此这里所谈的 配接器事实上是以容器的迭代器为间接参数。且C++语法并无所谓间接参数。稍后看实现代码。
  4. 所谓对迭代器的修饰,只是一种观念上的改变(赋值操作变成插入操作、前进变成后退、绑定到 特殊装置上…)。你可以千变万化地写出适合自己所用的任何迭代器。就这一点而言,为了将STL灵活运用于你的日常生活之中,iterator adapters的技术是非常 重要的。

下面是一个实例,集上述三种iterator adapters之运用大成:

#include<iterator>
#include<deque>
#include<algorithm>
#include<iostream>
usiang namspace std;int main()
{//将outite绑定到cout。每次对outite指派一个元素,就后接一个" ostream_iterator<int>outite(cout," ");
intia[] = { 0, 1, 2 , 3 , 4 , 5} ;
deque<int>id(ia, ia+6);
//将所有元素拷贝到outite(那么也就是拷贝到cout)
copy(id.begin(),id.end(),outite);//输出0 1 2 3 4 5
cout<<endl;//将ia[]的部分元素拷贝到id内。使用front_insert_iterator。
//注意,front_insert_iterqtor会将assign操作改为push_front操作// vector不支持push_front(),这就是本例不以vector为示范对象的原因。
copy(ia+1,ia+2,front_inserter(id));
copy(id.begin(),id.end(),outite);//1 0 1 2 3 4 5
cout<<endl;//将ia[]的部分元素拷贝到id内.使用back_insert_iterator
copy(ia+3,ia+4,back_inseter(id));
copy(id.begin(),id.end(),outite)// 1 0 1 2 3 4 5 3
cout<<endl;//搜寻元素5所在位置
deque<int>::iteratorite = find(id.begin(), id.end(), 5);
//将ia[]的部分元素拷贝到过内。使用insert_iterator
copy(ia+O, ia+3 , inserter(id,ite));
copy(id.begin(), id.end(),outite); // 1 0 1 2 3 4 0 1 2 5 3
cout<<endl; //将所有元素逆向拷贝到outite
// rbegin ()和rend()与reverse_iterator有关,见稍后源代码说明
copy (id.rbegin(), id.rend(), ouite);// 3 5 2 1 0 4 3 2 1 0 1
cout << endl; //以下,将inite绑定到cin。将元素拷贝到init,直到eos出现istream_iterator<int> inite(cin), eos; // eos : end-of-stream
copy(inite, eos, inserter(id, id.begin()));
//由于很难在键盘上直接输入end-of-stream(end-of-file)符号
//(而且不同系统的eof符号也不尽相同),因此,为了让上一行顺利执行,
//请单独取出此段落为一个独立程序,并准备一个文件,例如int.dat,内置
//32 26 99(自由格式),并在console之下利用piping方式执行该程序,如下:
// c:\>type int.dat | thisprog
//意思是将type int.dat的结果作为thisprog的输入
copy(id.begin(), id.end(), outite);// 32 26 99 1 0 1 2 3 4 0 1 2 5 3

1.3 应用于仿函数,function adapters

  1. functor adapters (亦称为function adapters)是所有配接器中数量最庞大的一 个族群,其配接灵活度也是前二者所不能及,可以配接、配接、再配接。这些配接 操作包括系结(bind)否定(negate), 组合(compose)、以及对一般函数或成员函数的修饰(使其成为一个仿函数)。
  2. C++Standard规定这些配接器的接口可由获得,SGI STL则将它们实际定义于<stl_function.h>。
  3. function adapters的价值在于,通过它们之间的绑定、组合、修饰能力,几乎可以无限制地创造出各种可能的表达式(expression), 搭配STL算法一起演出。
  4. 例如,我们可能希望找出某个序列中所有不小于12的元素个数。虽然,“不小于”就是“大于或等于”,我们因此可以选择STL内建的仿函数greater_equal,但如果希望完全遵循题目语意(在某些更复杂的情况下,这可能是必要的),坚持找出“不小于”12的元素,可以这么做:
not1(bind2nd(less<int>(),12))

这个式子将less()的第二参数系结(绑定)为12,再加上否定操作,便形成了“不小于12"的语意,整个凑和成为一个表达式(expression), 可与任何”可接受表达式为参数”之算法搭配,几乎每个STL算法都有这样的版本。

再举一个例子,假设我们希望对序列中的每一个元素都做某个特殊运算,这个 运算的数学表达式为:

f(g(elem))

其中f和g都是数学函数,那么可以这么写:

composel (f (x), g (y));

例如我们希望将容器内的每一个元素v进行(v+2)*3的操作,我们可以令f(x)= x*3, g(y)= y+2, 并写下这样的式子:

compose1(bind2nd(multiplies<int>(),3), bind2nd(plus<int>() ,2))
//第一个参数被拿来当做f(),第二个参数被拿来当做g()

这一长串形成一个表达式,可以拿来和任何接受表达式的算法搭配。不过,务请注意,这个算式会改变参数的值,所以不能和non-mutating算法搭配。例如不 能和for_each搭配,但可以和transform搭配,将结果输往另一地点。下面是 个实例:

#include<algorithm>
#include<functional>
#include<vector>
#include<iostream>
#include<iterator>
using namespace std;int main()
{//将outite绑定到cout,每次对outite指派一个元素,就后接一个" ",
ostream_iterator<int> outite(cout," ");int ia[6] = {2,21,12,7,19,23};
vector<int> iv(ia,ia+6);//II欲于每个元素v身上执行(v+2)*3.
//注意,for_each()是nonmutatingalgorithm. 元素内容不能更改
//所以,执行之后iv内容不变。
for_each(iv.begin(), iv.end(), composel(
bind2nd (multiplies<int>(),3),bind2nd(plus<int>(),2)));
copy(iv.begin(), iv.end(), outite);
cout<<endl;//2 21 12 7 19 23
//如果像这样,输往另一地点(cout), 是可以的
transform(iv.begin(), iv.end(), out1.te, composel(
bind2nd(multipies<int>(), 3),
bind2nd(plus<int>(), 2))) ;
cout<<endl; // 12 69 42 27 63 75
}
  1. 由于仿函数就是“将function call操作符重载"的一种class,而任何算法接受一个仿函数时,总是在其演算过程中调用该仿函数的operator(),这使得不具“备仿函数之形、却有真函数之实的“一般函数”和成员函数(memberfunctions)感到为难。
  2. 如果这些既存的心血不能纳入复用的体系中,完美的规划就崩落了一角。为此,STL又提供了为数众多的配接器,使”一般函数”和“成员函数”得以无缝隙地与其它配接器或算法结合起来。
  3. 当然,STL所提供的这些配接器不可能在变化纷歧的各种应用场合完全满足你的所有需求,例如它没有能够提供我们写出“大于5且小于10”或是“大于8或小于6”这样的算式。不过,对STL源代码有了一番彻底研究后,要打造专用的配接器,不是难事。
  • 请注意,所有期望获得配接能力的组件,本身都必须是可配接的(adaptable)。

    • 换句话说,一元仿函数必须继承自unary_function (7 .1.1节),二元仿函数必须继承自binary_function(7.1.2节),成员函数必须以mem_fun处理过,一般函数必须以ptr_fun处理过。

      • 一个未经ptr_fun处理过的一般函数,虽然也可以函数指针(pointer to function)的形式传给STL算法使用(STL算法接获一个表达式Op后,会以Op(…)的形式使用之。如果这个表达式是一个 函数指针,Op(…)仍能成立。这就是算法可以接受函数指针的原因。),却无法拥有任何配接能力。
      • 下图是STL function adapters一览表。实际运用时通常我们采用图左的辅助函数而不自行产生图右的对象,因为辅助函数的接口比较直观,比较好用;有些辅 助函数还形成重载(例如图下方的mem_fun()和mem—fun_ref()) , 更增加了 使用上的便利。

以下各挑一个配接器类型做示范:

#include <algorithm>
#include <funcional>
#include <vector>
#include <iostream>
using narnespace std;
//这里有个既存函数(稍后希望于STL体系中被复用)void print(int i){cout<<i<<' ';}
class Int
{public:
explicit Int(int i):m_i(i){}
//这里有个既存的成员函数(稍后希望于STL体系中被复用)
void print1()const{cout<<'['<<m_i<<'j';}
private:
int m_i;
};int main()
{//将outite绑定到cout。每次对outite指派一个元素,就后接一个" "
ostream_iterator<int>outie(cout," ");
int ia[6] = {2,21,12,7,19,23};
vector<int> iv(ia,ia+6);//找出不小于12的元素个数
cout<<count_if(iv.begin(),iv.end(),not1(bind2nd(less<int>(),12)));//4
cout<<endl;//令每个元素v执行(v+2)*3然后输往outite
transform(iv.begin(),iv.end(),outite,composel(bind2nd(multiplies<int>(),3),bind2nd(plus<int>(),2)));
cout<<endl;//12 69 42 27 63 75//以下将所有元素拷贝到outite. 有数种办法
copy(iv.begin(), iv.end(), outite);// 21 12 7 19 23cout << endl;
//(1)以下, 以函数指针搭配STL算法
for_each(iv.begin(), iv.end(), print);//  21 12 7 19 23
cout<<endl;// (2)以下, 以修饰过的一般函数搭配STL算法
for_each(iv.begin(), iv.end(), ptr_fun(print)); // 2 21 12 7 19 23
cout<< endl;
Int t1(3), t2 (7), t3(20), t4(14), t5 (68);
vector<Int> Iv;
Iv.push_back(t1);
Iv.push_back(t2);
Iv.push_back(t3) ;
Iv.push_back(t4);
Iv.push_back(t5) ;
//(3)以下, 以修饰过的成员函数搭配STL算法
for_each(Iv.begin() , Iv.end(), mem_fun_ref(&Int::print1));
// [3] [7] [20] [14] [68]
}

请注意,上述例子中,打印函数不能设计成这样:

class Int {
public:
void print2 (int i) { cout << '[' << l << ']'; }
...
} ;
for_each(Iv.begin () , Iv.end(), mem_funl_ref(&Int::print2));

因为这不符合for_each()的接口需求。

2.conrainer adapters

2.1 stack

stack的底层由deque构成。从以下接口可清楚看出stack与deque的关系:

template<class T, class Sequence= deque<T> >
class stack {
protected:
Sequence c;//底层容器
...
};
  1. C++ Standard规定客户端必须能够从中获得stack的接口,SGI STL 则把所有的实现细节定义于的<stl_stack.h>内,请参考4.5节。
  2. class stack封住了所有的deque对外接口,只开放符合stack原则的几个函数,所以我们说 stack是一个配接器,一个作用于容器之上的配接器。

2.2 queue
queue的底层由deque构成。从以下接口可清楚看出queue与deque的关系:

template <class T, class Sequence= deque<T> >
class queue {
protected:
Sequence c;//底层容器
...
};
  1. C++ Standard规定客户端必须能够从中获得queue的接口,SGI STL 则把所有的实现细节定义于<stl_queue.h>内,请参考4.6节。

  2. class queue封住 了所有的deque对外接口,只开放符合queue原则的几个函数,所以我们说 queue是一个配接器,一个作用于容器之上的配接器。

3.iterator adapters

3.1 insert iterators

  1. 下面是三种insert iterators的完整实现列表。 其中的主要观念是, 每 个 insert iterators内部都维护有一个容器(必须由用户指定);
  2. 容器当然有自己的迭代器,于是,当客户端对insert iterators做赋值(assign)操作时,就在insert iterators 中被转为对该容器的迭代器做插入(insert)操作,也就是说,在insert iterators的 operator=操作符中调用底层容器的push_front ()或push_back ()或insert () 操作函数。
  3. 至于其它的迭代器惯常行为如 operator++, operator++(int),operator* 都被关闭功能, 更没有提供operator–(int)或 operator-- 或 operator->等功能(因此被类型被定义为output_iterator_tag)。换句话说, insert iterators的前进、 后退、 取值、 成员取用等操作都是没有意义的, 甚至是不允许的。
//这是一个迭代器配接器(iterator adapter) , 用来将某个迭代器的赋值(assign)
//操作修改为插入(insert)操作:从容器的尾端插入进去(所以称为back_insert)
template <class Container>
class back_insert_iterator{protected:
Container* container; //底层容器
public:
typedef output_iterator_tag iterator_category;//注意类型
typedef void    value_type;
typedef void    difference_type;
typedef void    pointer;
typedef void    reference;//下面这个ctor使back_insert_iterator与容器绑定起来
explicit back_insert_iterator(Container& x) : container (&x) { } back_insert_iterator<Container>&
operator=(const typename Container: :value_type& value) {
container->push_bak(value);//这里是关键,转而调用push_back()
return *this;
}//以下三个操作符对back_insert_iterator 不起作用(关闭功能)
//三个操作符返回的都是back_insert_iterator自己
back_insert_iterator<Container>& operator*(){ return *this; }
back_insert_iterator<Container>& operator++(){ return *this; }
back_insert_iterator<Container>& operator++(int){ return *this; }
};//这是一个辅助函数,帮助我们方便使用back_insert_iterator
template <class Container>
inline back_insert_iterator<Container>back_inserter(Conta1ner& x) {return back_insert_iterator<Container>(x);
}//这是一个迭代器配接器(iterator adapter) , 用来将某个迭代器的赋值(assign)
//操作修改为插入(insert)操作一容器的头端插人进去(所以称为front insert)
//注意,该迭代器不适用于vector, 因为vector没有提供push_front 函数
template <class Container>
class front_insert_iterator {
protected:
Container* container; //底层容器
public:
typedef output_iterator_tag iterator_category;//注意类型
typedef void   value_type;
typedef void    difference_type;
typedef void    pointer;
typedef void    reference;explicit front_insert_iterator(Container& x) : container (&x) {} front_insert_iterator<Container>&
operator=(const typename Container::value_type& value) {
container->push_front (value); //这里是关键,转而调用push_front ()
return *this;
}//以下三个操作符对front_insert_iterator不起作用(关闭功能)
//三个操作符返回的都是front_insert_iterator自己。
front_insert_iterator<Container>& operator*(){return *this; }
front_insert_iterator<Container>& operator++(){return *this; }
front_insert_iterator<Container>& operator++(int){return *this; }
};
//这是一个辅助函数,帮助我们方便使用front_insert_iterator
template <class Container>
inline front_insert_iterator<Container>front_inserter(Container& x) {return front_insert_iterator<Container>(x);
}//这是一个迭代器配接器(iterator adapter),用来将某个迭代器的赋值(assign)//操作修改为插入(insert)操作, 在指定的位置上进行, 并将迭代器右移一个位置
//如此便可很方便地连续执行 ”表面上是赋值(覆写)而实际上是插入" 的操作
template <class Container>
class insert_iterator {
protected:
Container* container;//底层容器
public:
typedef output_iterator_tag iterator_category;//注意类型
typedef void   value_type;
typedef void    difference_type;
typedef void    pointer;
typedef void    reference;
insert_iterator(Container& x, typename Container::iterator i)
: container(&x), iter(i) {}
insert_iterator<Container>&
operator=(const typename Container::value_type& value) {
iter = container->insert(iter,value);//这里是关键,转调用insert()
++iter;//注意这个, 使insert iterator永远随其目标贴身移动
return *this;
}//以下三个操作符对insert_iterator不起作用(关闭功能)
//三个操作符返回的都是insert_iterator自己
insert_iterator<Container>& operator*(){ return *this; }
insert_iterator<Container>& operator++(){return *this; }
1nsert_iterator<Container>& operator++(int){return *this; }
};
//这是一个辅助函数, 帮助我们方便使用insert_iterator
template <class Container, class Iterator>
inline insert_iterator<Container>inserter(Container& x, Iterator i) {typedef typename Container::iterator iter;
return insert_iterator<Container>(x,iter(1));
}

3.2 reverse iterators

所谓reverse iterator, 就是将迭代器的移动行为倒转。如果STL算法接受的不 是一般正常的迭代器,而是这种逆向迭代器,它就会以从尾到头的方向来处理序列中的元素。 例如:

//将所有元素逆向拷贝到ite所指位置上
// rbegin()和rend()与reverse_iterator有关
copy (id.rbegin(), id.rend(), ite);

看似单纯, 实现时却大有文章。首先看看rbegin()和rend()。任何STL容器都提供有这两个操作,在第4,5两章介绍各种容器时,鲜有列出这两个成员函数,选择举个例子:

template<class T,class Alloc = alloc>//预设使用 alloc 为配置器
class vector{public:
typedef T value_type;
typedef value_type*iterator;
typedef reverse_iterator<iterator> reverse_iterator;
reverse_iterator  rbegin() {return reverse_iterator(end());}
reverse_iterator end() {return reverse_iterator(begin());}
...
};

再举个例子:

template <class T,class Alloc = alloc,size_t BufSiz=0>
class deque{public:
typedef __deque_iterator<T,T&,T*,BufSiz> iterator;
typedef reverse_iterator<iterator> reverse_iterator;
iterator begin() {return start;}
iterator end() {return finish;}
reverse_iterator rbegin() {return reverse_iterator(finish);}
reverse_iterator rend() { return  reverse_iterator(start);}
//上述两式相当于:
reverse_iterator rbegin() {return reverse_iterator(begin());}
reverse_iterator rend() { return  reverse_iterator(end());}
...
};

没有任何例外! 只要双向序列容器提供了begin(), end(), 它的 rbegin (),rend() 就是上面那样的型式。 单向序列容器如slist不可使用reserve iterators。有些容器如stack、queue、priority_queue并不提供begin(),end(),当然也就没有rbegin(),rend()。

现在延续1.2节的实例,假设有个deque id,当前的内容是:

32 26 99 1 0 1 2 3 4 0 1 2 5 3

执行以下操作:

cout<<*(id.begin())<<endl;//32
cout<<*(id.rbegin())<<endl;//3
cout<<*(id.end())<<endl;//0 dangerous!
cout<<*(id.rend())<<endl;//0 dangerous!deque<int>::iterator ite = find(id.begin(),id.end(),99);
reverse_iterator<deque<int>::iterator > rite(ite);
cout<<*ite<<endl;//99
cout<<*rite<<endl;//26

为什么“正向迭代器”和“与其相应的逆向迭代器”取出不同的元素呢?这并不是一个潜伏的错误,而是一个刻意为之的特征,主要是为了配合迭代器区间的“前闭后开“习惯(1.9.5节)。从下图的rbegin()和end()关系可以看出,当迭代器被逆转方向时,虽然其实体位置(真正的地址)不变,但其逻辑位置(迭代器所代表的元素)改变了(必须如此改变):

当迭代器被逆转,虽然实体位置不变,但逻辑位置必须如此改变

唯有这样,才能保持正向迭代器的一切惯常行为。换句话说,唯有这样,当我 们将一个正向迭代器区间转换为一个逆向迭代器区间后,不必再有任何额外处理, 就可以让接受这个逆向迭代器区间的算法,以相反的元素次序来处理区间中的每一 个元素。例如(以下出现于1.2节实例之中):

copy(id.begin(),id.end(),outite);//1 0 1 2 3 4 0 1 2  5 3
copy(id.rbegin(),id.rend(),outite);// 3 5 2 1 0 4 3 2 1 0 1

注意,上述的id.rbegin()是个暂时对象,相当于:

reverse_iterator<deque<int>::iterator>(id.end());//指向本例的最后元素
deque<int>::reverse_iterator(id.end());//指向本例的最后元素

其中的deque::reverse_iterator是一种型别定义,稍早已展现过。

下面是reverse_iterator的源代码:

//这是一个迭代器配接器(iterator adapter)用来将某个迭代器逆反前进方向:
//使用前进为后退,后退为前进
template <class Iterator>
class reverse_iterator
{protected:
Iterator current; //记录对应之正向迭代器
public:
//逆向迭代器的5种相应型别(associated types)都和其对应的正向迭代器相同
typedef typename iterator_traits<Iterator> ::iterator_category
iterator_category;
typedef typename iterator_titaits<Iterator>::value_type
value_type;
typedef typename iterator_titaits<Iterator>::difference_type
difference_type;
typedef typename iterator_trairs<Iterator>::pointer
pointer;
typedef typename iterator_trairs<Iterator>::reference
reference;typedef Iterator iterator_type; //代表正向迭代器
typedef reverse_iterator<Iterator> self;//代表逆向迭代器public:
reverse_iterator() {}
//下面这个ctor将reverse_iterator与某个迭代器x系结起来
explicit reverse_ierator(iterator_type x):current (x){}
reverse_iterator(const self& x):current(x.current){}
iterator_type base()const{ return current; } //取出对应的正向迭代器
reference operator*() const {
Iterator tmp = current;
return *--tmp;
//以上为关键所在。 对逆向迭代器取值, 就是将 “对应之正向迭代器” 后退一格而后取值
}
pointer operator->() const { return &(operator*()); } //意义同上
//前进(++)变成后退(--)
self& operator++() {
--current;
return *this;
}
self operator++(int){self tmp = *this;
--current;
return tmp;
}
//后退(--)变成前进(++)
self operator--(){++current;
return *this;
}
self operator--(){self tmp = *this;
++current;
return tmp;
}
//前进与后退方向完全逆转
self operator+(difference_type n)const{return self(current -n);
}
self& operator+=(difference_tyep n){current -=n;
return *this;
}
self operator-(difference_type n){current +=n;
return *this;
}
//注意,下面第一个*和唯一一个+都会调用本类的opearator*和opreator+,
//第二个*则不会。(判断法则:完全看处理的型别是什么而定)
reference operator[] (difference_typen) const { return *(*this + n);}
};

下面是另一些测试:

//容器目前状态:1 0 1 2 3 4 0 1 2 5 3
deque<int>::reverse_iterator rite2(id.end());
cout << *(rite2); // 3
cout<<*(++++++rite2); // 1 (前进3个位置后取值)
cout << *(--rite2);//2 (后退1个位置后取值)
cout << *(rite2.base());// 5 (恢复正向迭代器后,取值)cout << rite2[3];//4 (前进3个位置后取值)

下面以图形显示上述几个操作对逆向迭代器所造成的移动:

图片说明: rbegin ()连续三次累进(注++++++合乎C++语法)后,退后一格,然后再以注标表示法 [3]前进三格。由于是逆向迭代器,所以方向与一 般的正向迭代器恰恰反。

3.3 stream iterators

  1. 所谓stream iterators , 可以将迭代器绑定到一个stream (数据流)对象身上。 绑定到istream对象(例如std::cin)者,称为istream_iterator, 拥有输入能力;绑定到ostream对象(例如std::cout)者,称为 ostream_iterator , 拥有输出能力。两者的用法在1.2节的例子中都有示范。
  2. 所谓绑定一个istream object, 其实就是在istream iterator内部维护一个istream member, 客户端对于这个迭代器所做的operator++操作,会被导引调用迭代器内部所含的那个istream member的输入操作 (operator>>)。这个迭代器是个Input Iterator, 不具备operator–。下面的源 代码和注释说明了 一 切。
//这是一个input iterator, 能够为 ”来自某一 basic_istream"的对象执行
//格式化输入操作。注意,此版本为旧有之HP规格,未符合标准接口:
// istream_iterator<T, charT, traits, Distance>
//然而一般使用input iterators时都只使用第一个template参数,此时以下仍适用//注: SGI STL 3.3已实现出符合标准接口的istream_iterator。做法与本版大同小异//本版可读性较高
template <class T, class Distance= ptrdiff_t>
class istream_iterator {
friend bool
operator==__STL_NULL_TMPL_ARGS(const istream_iterator<T,Distance>& x,const istream_iterator<T,Distance>& y);
//在<stl _config.h>中,__STL_NULL_TMPL_ARGS被定义为<>
protected:
istream* stream;
T value;
bool end_marker;
void read(){end_marker = (*stream)?true : false;
if(end_marker)*stream >> value;//关键
//以上, 输入之后,stream的状态可能改变, 所以下面再判断一次以决定end_marker
//当读到eof或读到型别不符的资料, stream即处于false状态end_marker = (*stream) ? true : false; }public:
typedef input_iterator_tag  iterator_category;
typedef T   value_type;
typedef Distance    difference_type;
typedef const T*    pointer;
typedef const T&    reference; //以上, 因身为input iterator, 所以采用canst比较保险
istream_iterator() : stream(&cin), end_marker(false) {}
istream_iterator(istream& s) : stream(&s) {read(); }//以上两行的用法://istream_iterator<int> eos;//造成end_marker为false//istream_iterator<int> initer(cin);//引发read(),程序至此会等待输入//因此,下面这两行客户端程序://istream_iterator<int> initer(cin);  (A)//cout<<"please input ..."<<endl;        (B)//会停留在(A)等待一个输入,然后才执行(B)出现提示信息。这是不合理的现象
//规避之道:永远在最必要的时候, 才定义个istream_iterator。
reference operator*()const{ return value; }
pointer operator->()const { return &(operator*()); } //迭代器前进一个位置, 就代表要读取笔资料
istream_iterator<T, Distance>& operator++() {
read();
return *this;
}
istream_iterator<T,Distance>operator++(int){istream_iterator<T,Distance> tmp =*this;
read();
return tmp;
}
};

请注意, 源代码清楚告诉我们, 只要客户端定义个istream iterator并绑定到某个 istream object, 程序便立刻停在istream_iterator<T>: : read ()函数,等待输入。 这不是我们所预期的行为, 因此,请在绝对必要的时刻才定义你所需要的istream iterator:这其实是C++程序员在任何时候都应该遵循的守规,但是很多人忽略了。

下图所示是copy()和istream_iterator共同合作的例子:

  1. copy()和istream_iterator合作。屏幕画面下方的浅蓝色方块是客户端程序代码,程序流程将停在其中第一行,等待用户从cin输入(因为右侧的istream_iterator源代码格式我们,一产生istream_iterator对象,便会调用read(),执行*stream>>value,而*stream在本例就是cin)。用户输入值后,copy算法(源代码见最下方块内容)将该数值插入到容器之内,然后执行istream_iterator的operator++操作:这又再次引发istream_iterator::read()
  2. 浅色底纹部分为客户端程序代码。 另两块分别为istream_iterator 和copy()的源代码。 copy()算法已于6.4.2节有过详细的介绍, 它有能力判断各种迭代器类型,采用最佳处理方式, 由于istream_iterator是个 lnputIterator, 所以copy ()最后会进人图下方所摘录的那段代码内。*result = *first; // first是一个istream_iterator object
  3. 根据istream_iterator的定义, 对first取值, 就是返回data member value, 也就是刚才从cin获得的值。此值于是被指派给 *result。当copy()中 的for循环进人下一次迭代时,会引发++first, 而根据istream_iterator的 定义, 对first累加, 就是再从cin中读一个值(放入data member value中), 然后又是 *result= *first; 如此持续下去, 直到first==last为止。 last代表的是一个end-of-stream标记, 在各个系统上可能都不相同。

以上便是istream iterator的讨论。 至于ostream iterator, 所谓绑定 一 个 ostream object, 就是在其内部维护一个ostream member, 客户端对于这个迭代器所做的operator=操作,会被导引调用对应的(迭代器内部所含的)那个ostream member的输出操作(operator<<)。 这个迭代器是个Outputlterator。 下面的源 代码和注释说明了 一 切。

//这是一个output iterator, 能够将对象格式化输出到某个basic_ostream上
//注意, 此版本为旧有之 HP 规格, 未符合标准接口:
// ostream_iterator<T, charT, traits>
//然而一般使用onput iterators时都只使用第一个template参数, 此时以下仍适用
//注: SGI STL 3.3已实现出符合标准接口的ostream_iterator。 做法与本版大同小异
//本版可读性较高
template <class T>
class ostream_iterator{
protected:
ostream* stream;//每次输出后的间隔符号
const char* string;//变量名称为string可以吗?可以!public:
typedef output_iterator_tag     iterator_category;
typedef void    value_type;
typedef void    difference_type;
typedef void    pointer;
typedef void reference;ostream_iterator(ostream& s):stream(&s),string(O) {}
ostream_iterator(ostream& s, const char* c) : stream(&s), string(c) {}
//以上ctors的用法:
//ostream_iterator<int>outiter(cout,' ');输出至cout。每次间隔一空格//对迭代器做赋值(assign)操作,就代表要输出笔资料
ostrearn_iterator<T>& operator=(const T&value) {*stream<< value;  //关键:输出数值
if(string)*stream<< string; //如果输出状态无误,输出间隔符号
return *this;
}
//注意以下三个操作
ostrearn_iterator<T>& operator*(){ return *this; }
ostrearn_iterator<T>&operator++(){ return*this; }
ostrearn_iterator<T>& operator++(int){ return *this; }
};
  1. 下图是copy()和ostream_iterator共同合作的例子。此图浅色底纹部分为客户端程序码。另两块分别为ostream_iterator和copy()的源代码。 本例令ostream_iterator绑定到标准输出设备cout身上。copy()算法已于6.4.2节有过详细的介绍,它有能力判断各种迭代器类型,采用最佳处理方式,由于deque::iterator是个RandomAccesslterator,所以copy()最后会*result=*first; // result是一个ostream_iteratorobject.
  2. 根据ostream_iterator的定义,对result取值,返回的是自己。对result执行赋值(assign)操作,则是将operator=右手边的东西输出到cout去。当copy()算法进入for循环的下次迭代时,会引发++result,而根据 ostream_iterator的定义,对result累加,返回的是自己。如此持续下去,直 到资料来源结束(first==last)为止。


这两个迭代器,istream_iterator和ostream_iterator,非常重要。不是说它们在应用上非常重要,而是说这两个迭代器的源代码向我们展示了如何为自己量身定制一个迭代器,系结(绑定)于你所属意的装置上。正如我在本章一开始的概观说明中提过,以这两个迭代器的技术为蓝图,稍加修改,便有非常大的 应用空间,可以完成一个绑定到Internet Explorer cache身上的迭代器,也可以完成一个系结到磁盘目录上的一个迭代器,还有很多,泛型世界无限广阔。

4.function adapters

  1. 对于C++ template语法有了某种程度的了解之后,我们很能够理解或想象,容器是以class templates完成,算法以function templates完成,仿函数是一种将operator()重载的class template, 迭代器则是一种将operator++ 和operator*等指针习惯常行为重载的class template。
  2. 然而配接器呢?应用于容器身上和迭代器身上的配接器,已于本章稍早介绍过,都是一种class template。可 应用于仿函数身上的配接器呢?如何能够”事先”对一个函数完成参数的绑定、执行结果的否定、甚至多方函数的组合?
  3. 注意我用”事先”一词。意思是,最后修饰结果(视为一个表达式,expression)将被传给STL算法使用,STL算法 才是真正使用这表达式的主格。而我们都知道,只有在真正使用(调用)某个函数(或仿函数)时,才有可能对参数和执行结果做任何干涉。

这是怎么回事?

  1. 关键在于,就像本章先前所揭示,container adapters内藏了一个container member一样,或是像reverse iterator (adapters)内藏了一个iterator member一样, 或是像stream iterator (adapters)内藏了一个pointerto stream一样,或是像insert iterotor(adapters)内藏了一个pointer to container(并因而得以取其iterator)一样, 每一个function adapters也内藏了一个member object, 其型别等同于它所要配接 的对象(那个对象当然是一个“可配接的仿函数", adaptable functor) , 下图是一份整理。
  2. 当function adapter有了完全属于自己的一份修饰对象(的副本)在手, 它就成了该修饰对象(的副本)的主人,也就有资格调用该修饰对象(一个仿函数), 并在参数和返回值上面动手脚了。

下图鸟瞰了count_if ()搭配bind2nd(less (), 12))的例子,其中清楚显示count_if()的控制权是怎么落到我们的手上。控制权一旦在我们手上, 我们当然可以予取予求了。

鸟瞰count_if()和bind2nd(less () , 12))的搭配实例。此图等于是相关源代码的接口整理,搭配bind2nd()以及class binder2nd源代码 。图中浅色底纹方块为客户端调用count_if()实况;循着箭头行进,便能理解的整个合作机制。

4.1 对返回值进行逻辑否定:not1,not2

以下直接列出源代码。源代码中的注释配合先前的概念解说,应该足以让你彻 底认识这些仿函数配接器。源代码中常出现的pred一词,是predicate的缩写, 意指会返回真假值(bool)的表达式。

//以下配接器用来表示某个Adaptable Predicate的逻辑负值(logical negation)template <class Predicate>
class unary_negate
:public unary_function<typename Predicate::argument_type,bool>
protected:
Predicate pred;//内部成员
public:
explicit unary_negate(const Predicate& x):pred(x){}
bool operator() (const typename Predicate::argument_type& x)const
{return !pred(x);//将pred的运算结果加否定(negate)运算
}
};//辅助函数,使我们得以方便使用unary_negate<Pred>
template <class Predicate>
inline unary_negate<Predicate> not1(const Predicate& pred) {return unary_negate<Predicate>(pred);
}//以下配接器用来表示某个Adaptable Binary Predicate的逻辑负值
template<class Predicate>
class binary_negate
: public binary_function <typename Predicate::first_argument_type,typename Predicate::seconde_argument_tyep,bool>{protected:
Predicate pred;//内部成员
public:
explicit binary_negate(const Predicate& x):pred (x) {}
bool operator()(const typename Predicate::first_argument_type& x,
const typename Predicate::second_argument_type& y) const {return !pred(x, y); //将pred的运算结果加上否定(negate)运算
}
};
//辅助函数,使我们得以方便使用bineary_negate<Pred>
template<class Predicate>
inline binary_negate<Predicate> not2(const Predicate& pred) {return binary_negate<Predicate>(pred);
}

4.2 对参数进行绑定:bind1st,bind2nd

以下直接列出源代码。源代码中的注释配合先前的概念解说,应该足以让你彻底认识这些仿函数配接器。

//以下配接器用来将某个Adaptable Binary function转换为Unary Function
template<class Operator>
class binder1st
:public unary_function<typename Operation::second_argument_type,typename Operation::result_type>
{protected:
Operation op;//内部成员
typename Operation::first_argument_type value;//内部成员
public:
//constructor
binder1st(const Operation& x,const typename Operation::first_argument_type& y)
:op(x),value(y){}//将表达式和第一参数记录于内部成员
typename Operation::result_type
operator() (const typename Operation::second_argument_type& x) const {return op(value,x);//实际调用表达式,并将value绑定为第一参数
}
};//辅助函数, 让我们得以方便使用binder1st<Op>
template<class Operation,class T>
inline binder1st<Operation> bind1st(const Operation& op,const T& x)
{typedef typename Operation::first_argument_type argl_type;
return binder1st<Operation>(op,arg1_type(x));
}//以下配接器用来将某个Adaptable Binary function转换为Unary Function
template <class Operation>
class binder2nd
: public unary_function<typename Operation::first_argument_type,typename Operation::result_type>{protected:
Operation op;//内部成员
typename Operation::second_argument_type value;//内部成员
public:
//construct
binder2nd(const Operation x,const typename Operation::second_argument_type& y)
:op(x),value(y){}//将表达式和第一参数记录于内部成员
typename Operation::result_type
operator()(const typename Operation::first_argument_type& x)const
{return op(x,value);//实际上调用表达式,并将value绑定为第二参数
}
};
//辅助函数,让我们得以力便使用binder2nd<Op>template <class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& op, const T& x)
{typedef typename Operation::second_argument_type arg2_type;
return binder2nd<Operation>(op, arg2_type(x));
//以上,注意,先把x转型为op的第二参数型别
}

4.3 用于函数合成compose1,compse2

以下直接列出源代码。源代码中的注释配合先前的概念解说,应该足以让你彻底认识这些仿函数配接器。请注意,本节的两个配接器并未纳入STL标准,是SGI STL的私产品,但是颇有钻研价值,我们可以从中学习如何开发适合自己的、更复杂的配接器。

//已知两个Adaptable Unary Functions f() ,g(), 以下配接器用来产生一个h(),
//使h(x)= f(g(x))
template<class Operation1, class Operation2>
class unary_compose
:public unary_function<typename Operation2::argument_type,typename Operation1::result_type>{protected:
Operation1 op1;//内部成员
Operation2 op2;//内部成员
public:
//constructor
unary_compose(const Operation1& x,const Operation2& y)
:op1(),op2(y){}//将两个表达式记录于内部成员
typename Operation1::result_type
operator()(const typename Operation2::argument_type& x)const{return op1(op2(x));//函数合成
}
}//辅助函数,让我们得以方便运用unary_compose<Op1,Op2>
template <class Operation1, class Operation2>
inline unary_compose<Operation1, Operation2>
compose1(const Operation1& op1, const Operation2& op2) {return unary_compose<Operation1,Operation2>(op1,op2);
}//已知一个Adaptable Binary Function f 和两个Adaptable Unary Function g1,g2
//以下配接器用来产生一个h, 使h(x)=f(g1(x) ,g2(x)) template <class Operation1, class Operation2, class Operation3>
class binary_compose
:public unary_function<typename Operation2::argument_type,typename Operation1::result_type>{protected:
Operation1 op1;//内部成员
Operation2 op2;//内部成员
Operation3 op3;//内部成员
public:
// constructor, 将三个表达式记录千内部成员
binary_compose(const Operation1& x, const Operation2& y,const Operation3& z):op1(x),op2(y),op3(z){}typename Operation1::result_type
operator()(const typename Operation2: :argument_type& x) const
{return opl (op2 (x), op3 (x)); //函数合成
}
};//辅助函数, 让我们得以方便运用binary_compose<Op1,Op2,0p3>
template <class Operation1, class Operation2, class Operation3>
inline binary_compose<Operation1, Operation2 Operation3>
compose2(const Operation1& op1, const Operation2& op2, const Operation3& op3){return binary_compose<Operation1, Operation2, Operation3>(op1,op2,op3);
}

4.4 用于函数指针:ptr_fun

  1. 这种配接器使我们能够将一般函数当做仿函数使用。
  2. 一般函数当做仿函数传给 STL算法,就语言层面本来就是可以的,就好像原生指针可被当做迭代器传给STL 算法样。
  3. 但如果你不使用这里所说的两个配接器先做一番包装,你所使用的那个一 般函数将无配接能力, 也就无法和前数小节介绍过的其它配接器接轨。

以下直接列出源代码。源代码中的注释配合前数小节的概念解说,应该足以让 你彻底认识这些仿函数配接器。

//以下配接器其实就是把一个一元函数指针包起来:
//当仿函数被使用时,就调用该函数指针
template <class Arg, class Result>
class pointer_to_unary_function: public unary_function<Arg, Result>
{protected:
Result (*ptr) (Arg); //内部成员, 一个函数指针
public:
pointer_to_unary_function() {}
//以下constructor将函数指针记录于内部成员之中
explicit pointer_to_unary_function(Result (*x)(Arg)):ptr(x){}
//以下, 通过函数指针执行函数
Result operator()(Arg x) const{return ptr(x);}
};//辅助函数,让我们得以方便运用pointer_to_unary_function
template <class Arg, class Result>
inline pointer_unary_function<Arg,Result> //灰色部分是返回值型别
ptr_fun(Result (*x)(Arg)) {
return pointer_to_unary_function<Arg,Result>(x);
}//以下配接器其实就是把一个二元函数指针包起来;
//当仿函数被使用时,就调用该函数指针
template <class Arg1, class Arg2, class Result>
class pointer_to_binary_function
: public binary_function<Arg1, Arg2, Result> {
protected:
Result (*ptr)(Arg1, Arg2); //内部成员, 一个函数指针
public:
pointer_to_binary_function() {}
//以下construetor将函数指针记录千内部成员之中
explicit pointer_to_binary_function(Result (*x) (Arg1, Arg2)):ptr(x){}///以下, 通过函数指针执行函数
Result operator() (Arg1 x, Arg2 y)const{return ptr(x, y);}
};
//辅助函数,让我们得以方便使用pointer_to_binary_function
template <class Arg1, class Arg2, class Result>
inline pointer_to_binary_function<Arg1,Arg2,Result> //返回值型别
ptr_fun(Result (*x)(Arg1, Arg2)) {
return pointer_to_binary_function<Argl, Arg2, Result>(x);
}

4.5 用于成员函数指针:mem_fun,mem_fun_ref

这种配接器使我们能够将成员函数(member functions)当做仿函数来使用,于是成员函数可以搭配各种泛型算法。当容器的元素型式是X&X*,而我们又以虚拟(virtual)成员函数作为仿函数,便可以藉由泛型算法完成所谓的多态调用(polymorphic function call)。这是泛型(genericity)与多态(polymorphism)之间的一个重要接轨。下面是一个实例,下图是例中的类阶层体系(classes hierarchy) 和实际产生出来的容器状态:

#include<iostream>
#include<vector>
#include<algorithm>
#include<functionl>
uiang namespace std;class Shape{public:
virtual void display()=0;
};class Ret:public Shape
{public:virtual void display(){cout<<"Rect";}
};class Circle:public Shape
{public:virtual void display(){cout<<"Circle";}
};class Sare: public Rect
{public: virtual void display() { cout << "Square "; }
};int main()
{vector<Shape*> V;
V.push_back(new Rect);
V.push_back(new Circle);
V.push_back(new Square);
V.push_back(new Circle);
V.push_back(new Rect);
// polymorphically
for(int i=O; i< V.size(); ++i)
(V[i])->display();
cout<<endl;//Ret Circle Square Circle Rect//polymorphically
for_each(V.begin(), V.end(), mem_fun(&Shape::display));
cout << endl; // Rect Circle Square Circle Rect
}

注意,对语法而言,不能这么写:

for_ech(V.begin(),V.end(),&Shape::display);

也不能这么写:

for_each(V.begin(),V.end(),Shape::display);

一定要以配接器mem_fun修饰member function, 才能被算法for_each接受。

另一个必须注意的是,虽然多态(polymorphism)可以对pointer或reference起作用,但很可惜的是,STL容器只支持“实值语意"(value semantic) , 不支持 “引用语意" (reference semantics) , 因此下面这样无法通过编译:

vector<Shape&> V;

以下是各个adapters for member function的源代码 , 源代码中的注释配合前数小节的概念解说, 应该足以让你彻底认识这些仿函数配接器。

//Adapter function objects: pointers to member functions.
//这个族群一共有8 = 2^3个function objecs。//(1)"无任何参数"vs "有一个参数”
//(2)通过pointer调用"VS "通过reference调用“
//(3)"const成员函数" vs "non-const成员函数”///所有的复杂都只存在于 function objects 内部。 你可以忽略它们,直接使用
//更上层的辅助函数 mem_fun 和 mem_fun_ref, 它们会产生适当的配接器//"无任何参数” 、 “通过pointer调用 “ 、 "non-const成员函数”template <class S, class T>
class mem_fun_t : public unary_function<T*, S> {public: explicit mem_fun_t(S (T::*pf)()) : f(pf) {} //记录下来
S operator()(T* p) const {return(p->*f)();}//转调用
private:
S (T::*f)();//内部成员, pointer to member function
};// “无任何参数” 、 “通过pointer调用“ 、 "const成员函数”template <class S, class T>
class const_mem_fun_t : public unary_funct <const T*, S> {public:
explicit const_mem_fun_t(S (T ::*pf) const): f(pf) {
S operator()(const T* p) const{ return (p->*f)();}
private:
S (T::*f)() const; //内部成员, pointer to const member function
};//"无任何参数” 、 “通过reference调用 ” 、 "non-const成员函数”
template <class S, class T>
class mem_fun_ref_t:public unary_function<T, S> {
public:
explicit mem_fun_ref_t(S (T::*pf)()):f(pf){} //记录下来
S operator()(T& r) const { return (r.* f) () ; }// 转调用private:
S (T::*f)();//内部成员, pointer to member function
};// "无任何参数” 、 “通过reference调用 “ 、 "const成员函数”template <class S, class T>
class const mem_fun_ref_t : public unary_function<T, S> {
public:
explicit const_mem_fun_ref_t(S,(T::*pf)()const):f(pf){}
S operator()(const T& r)const{return (r.*f)();}
private:
S (T::*f)()const;//内部成员 pointer to const memeber function
};// "有一个参数” 、 “通过pointer调用 “ 、 "non-const成员函数”
template <class S, class T, class A>
class mem_funl_t: public binary_function<T*, A, S> {public:
explicit mem_funl_t(S (T::*pf)(A}):f(pf){} //记录下来
S operator() (T* p, A x) const { return (p->*f) (x); }//转调用private:
S (T::*f)(A);//内部成员,pointer to member function
};//"有一个参数” 、 “通过pointer调用 “ 、 "const成员函数”
template <class S, class T, class A>
class const_mem_funl_t : public binary_function<const T*,A,S>{public:
explicit const_mem_fun1_t(S(T::*pf)const):f(pf){}
S operator()(const T* p,A x)const{return (p->*f)(x);}
private:
S (T::*f)(A)const;//内部成员 pointer to const member function
};//"有一个参数” 、 “通过reference调用“ 、 "non-cont成员函数”
template <class S, class T, class A>
class mem_funl_ref_t : public binary_function<T, A, S> {
public:
explicit mem_funl_ref_t(S,(T::*pf)(A)):f(pf){}//记录下来
S operator()(T& r,A x)const {return (r.*f)(x);}//转调用
private:
S (T::*f)(A);//内部成员 pointer to member function
};//"有一个参数” 、 “通过reference调用 “ 、 "const成员函数”
template <class S, class T, class A>
class const_mem_funl_ref_t : public binary_function<T, A, S> {
public:
explicit const_mem_funl_ref_t(S (T::*pf(A)const) : f(pf){}
S operator()(const T& r,A x)const{retrun (r.*f)(x);}
private:
S (T::*f)(A)const;//内部成员,pointer to const member function
};// mem_fun adapter的辅助函数:mem_fun, mem_ fun_ref
template <class S, class T>
inline mem_fun_t<S,T> mem_fun(S (T::*f)()){return mem_fun_t<S,T>(f);
}
template <class S, class T>
inline const_mem_fun_t<S,T> mem_fun(S (T:: * f)()const)
{return const_mem_fun_t <S, T> (f) ;
}template <class S, class T>
inline mem_fun_ref_t<S,T> mem_fun_ref(S (T::*f)())
{return mem_fun_ref_t<S,T>(f);
}template <class S, class T>
inline const_mem_fun_ref_t<S,T> mem_fun_ref(S {T::*f) () const)
{return const_mem_fun_ref_t<S,T>(f);
}//注意:以下四个函数, 其实可以采用和先前(以上)四个函数相同的名称(函数重载)。
//事实上C++ Standard也的确这么做。 G++ 2.91.57并未遵循标准,不过只要
//把mem_funl()改为mem_fun(), 把mem_funl_ref()改为mem_fun_ref(),
//即可符合C++ Standard。SGI STL 3.3就是这么做。
template <class S, class T, class A>
inline mem_funl_t<S,T,A> mem_funl (S (T:: *f) (A)) {
return mem_fun1_T<S,T,A>(f);
}template<class S,class T,class A>
inline mem_funl_ref_t<S,T,A>mem_funl_ref(S (T::*f)(A)){return mem_funl_ref_t<S,T,A>(f);
}tempalte<class S,class T,class A>
inline const_mem_funl_ref_t<S,T,A>mem_funl_ref(S (T::*f)(A)const){return const_meme_funl_ref_t<S,T,A>(f);
}

第八章 配接器 adapters相关推荐

  1. STL源码剖析 第八章 配接器

    设计模式:将一个类的接口转化为另外一个类的接口 配接器的概观和分类 改变仿函数接口  函数配接器  :queue和stack 通过修饰deque函数接口来实现 改变容器接口      容器配接器  : ...

  2. STL——配接器(adapters)

    一.配接器 <Design Patterns>一书提到23个最普及的设计模式,其中对adapter样式的定义如下:将一个class的接口转换为另一个class 的接口,使原本因接口不兼容而 ...

  3. STL源码剖析 配接器

    配接器(adapters)在 STL组件的灵活组合运用功能上,扮演着轴承.转换器的角色.Adapter这个概念,事实上是一种设计模式(design pattern). «Design Patterns ...

  4. 标准模板库(STL)之配接器(Adapter)

    一般只有一个私有成员变量(某物)的类,且其全部的成员函数都是对该唯一的成员变量的存.取和修改(实现修改某物接口,形成另一种风貌,侯捷语),则该类即为对该私有成员变量的配接(adapter). Adap ...

  5. STL学习_配接器篇

    STL学习_配接器篇 定义 配接器(Adapter)在STL组件的灵活组合运用功能上,扮演着轴承.转换器的角色.它事实上是一种设计模式.即将一个class的接口转换为另一个class的接口,使原本因接 ...

  6. STL源码剖析之配接器

    adapter(配接器)在STL组件的灵活组合运用上,扮演者转换器的角色.adapter来源于一种适配器模式,其功能是:将一个class接口转换为另一个class的接口,使得原本因接口不兼容而不能合作 ...

  7. Windows 10电脑WiFi标志消失(WLAN配适器消失),电脑网络栏无WIFI,且以太网配置也出了问题

    问题描述: WLAN配适器消失 以太网配置消失 问题排查开始 1.第一步查看驱动是否有问题- 网卡驱动 <1>找此电脑 <2>-点击鼠标右键,找到"属性" ...

  8. 计算机网络配适器型号在哪里看,网络适配器型号在哪看_怎么更换网络适配器...

    描述 网络适配器型号在哪看 在电脑上都会有网络适配器,很多朋友不知道在哪里查看网络适配器型号,下面小编就把步骤分享给大家. 1.首先找到桌面上的"我的电脑"(计算机),然后右键点击 ...

  9. WiFi配适器Linux,centos7 打开wifi适配器(wifiadapter)和安装centos7 需要注意的

    刚刚安装好centos7.6和windows10双系统,但是在打开centos10 之后 显示- no wifi-adaper found ,折腾了很久,终于在 https://unix.stacke ...

  10. c++ stl stack(FILO,容器配接器)

    1.stack stack是一种先进后出的数据结构,它只有一个出口.stack允许新增元素.移除元素.取得最顶元素.但除了最顶端外,没有任何其他方法可以存取stack的其他元素.换言之,stack不允 ...

最新文章

  1. 动手---sbt(2)
  2. matlab机械臂工作空间代码_【ROS-Moveit!】机械臂控制探索(3)——基于python的API示例代码分析...
  3. “约见”面试官系列之常见面试题之第九十二篇之created和mounted区别(建议收藏)
  4. Python中判断两个字符串的内容是否相同
  5. python学习笔记——叁之修改配置文件
  6. “7天”连锁酒店郑南雁
  7. MapGIS制图学习(1)
  8. 开放低代码的钉钉,能否普惠1700万企业?
  9. adb 查看屏幕大小_Android adb获取屏幕分辨率
  10. 为何数据分析师更容易获得高薪工作?
  11. 中华瑰宝之山西人的面食
  12. Adobe Reader PDF阅读器闪退问题解决(批处理)
  13. wps对父母子女身高数据集的线性回归处理
  14. 吉林大学珠海学院论坛 http://j.bnubbs.cn
  15. 368 银河(强连通分量)
  16. DevOps工具链学习——相关工具知多少
  17. selvert 小解
  18. 微型计算机中主板上的主桥,什么是微型计算机一级维修与二级维修如何对主板进行二级维修...
  19. python特征重要性_特征重要性--feature_importance
  20. 小米笔记本指纹识别失效解决方法

热门文章

  1. 智能名片小程序开发文档概要
  2. 名悦集团:车上不能缺的行车小物件,安全第一条
  3. std::ios_base::fmtflags orig std::streamsize prec
  4. ORA-12514 错误的处理
  5. Ora-12514原因及处理方法
  6. python时区转换_Python pytz时区转换
  7. 单片机系统的电磁干扰要如何消除?
  8. Java课程设计报告——学生成绩管理系统
  9. 腾讯应用宝正式开启 比赛进行到第手游渠道
  10. 阿里双十一购物节背后的技术问题