建设银行网站app查卡号/百度官网网址
STL之适配器adapters
- container adapers
- stack
- queue
- iterator adaptgers
- insert iterators
- reverse iterators
- stream iterators
- function adapters
- 对返回值进行逻辑判断:not1,not2
- 对参数进行绑定:bind1st, bind2nd
- 用于函数合成:compose1,compose2
- 用于函数指针 ptr_func
- 用于成员函数指针:mem_fun,mem_fun_ref
container adapers
stack
stack的底层由dequeu构成。从以下接口可清楚看出stack与deque的关系:
template <class T, class Sequence = deque<T> >
class stack {
protected:Sequence c; // 底层容器...
};
C++ Standard规定客户端必须能够从<stack>中获得stack的接口,SGI STL则把所有的实现细节定义于<stl_stack.h>中,参考stack。class stack封住了所有deque对外接口,只开放符合stack原则的几个函数,所以我们说stack是一个适配器,一个作用于容器的适配器。
queue
queue的底层实现也是deque,从以下接口可以清楚看出queue与deque的关系:
template <class T, class Sequence = deque<T>>
class queue {protected:Sequence c; // 底层容器...
};
C++ Standard规定客户端必须能够从<queue>中获得queue的接口,SGI STL则把所有的实现细节定义于<stl_queue.h>内,参见queue。class queue屏蔽了所有deque对外的接口,只开放符合queue原则的几个函数,所以我们说queue是一个适配器,一个作用于容器之上的适配器。
iterator adaptgers
本章稍早已说过iterator adapters的意义和用法,以下研究其实现细节。
insert iterators
下面三种insert iterators的完整实现列表表。其中的主要观念是,每一个insert iterators内部都维护有一个容器(必须由用户指定):容器当然有自己的迭代器,于是,当客户端对insert iterators做赋值(assgin)操作时,就在insert iterators的operator=操作符中调用底层容器的push_front()或push_back()或insert()操作函数。至于其它的迭代器惯常行为如operator++,operator++(int),operator*都被关闭(只保留了可编译通过的语法,无实际意义),更没有提供operator- -或operator- -(int)或operator->等功能(因此类型被定义为output_iterator_tag)。换句话说,insert iterators的前进、后退、取值、成员取用等操作都没有意义的,甚至是不被允许的。
代码如下所示:
back_inserter
// 这是一个迭代器适配器,用来将某个迭代器的赋值操作
// 修改为插入操作--从容器的头端插入进去(所以称为front_insert)
template <class Container>
class front_insert_iterator {
protected:Container * container;
public:typedef ouput_iterator_tag iterator_categroy; // 注意型别typedef void value_type;typedef void difference_type;typedef void pointer;typedef void reference;// 下面这个ctor使front_insert_iterator与容器绑定起来explicit front_insert_iterator(Container& x) : container(&x) {}front_insert_iterator<Container>&operator=(const typename Container::value_type& value) {container->push_front(value);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);
}
front_inserter
// 这是一个迭代器适配器,用来将某个迭代器的赋值操作
// 修改为插入操作--从容器的头端插入进去(所以称为front_insert)
template <class Container>
class front_insert_iterator {
protected:Container * container;
public:typedef ouput_iterator_tag iterator_categroy; // 注意型别typedef void value_type;typedef void difference_type;typedef void pointer;typedef void reference;// 下面这个ctor使front_insert_iterator与容器绑定起来explicit front_insert_iterator(Container& x) : container(&x) {}front_insert_iterator<Container>&operator=(const typename Container::value_type& value) {container->push_front(value);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);
}
注意因为vector没有push_front()接口,所以front_inserter不适合vector
inert_iterator
// 这是一个迭代器适配器(iterator adapter),用来将某个迭代器的赋值操作
// 修改为插入操作--在指定的位置上进行,并将迭代器右移一个位置
// --如此便可很方便地连续执行”表面上是赋值而实际上是插入“的操作
template <class Container>
class insert_iterator {
protected:Container * container;typename Container::iterator iter;
public:typedef ouput_iterator_tag iterator_categroy; // 注意型别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);++iter; // 注意此处有++操作,使insert iterator永远随目标贴身移动return *this;}// 以下三个操作符对insert_iterator不起作用(关闭功能)// 三个操作符返回的都是insert_iterator自己insert_iterator<Container>& operator*() {return *this; }insert_iterator<Container>& operator++() {return *this; }insert_iterator<Container>& operator++(int) {return *this; }
};// 这是一个辅助函数,帮助我们方便使用insert_iterator
template <class Container, class Iterator>
inline insert_iterator<Container> inserter(Container& x, Iterator i) {return insert_iterator<Container>(x, i);
}
reverse iterators
所谓revers iterator,就是将迭代器的移动行为倒转。如果STL算法接受的不是一般的正常迭代器。而是这种逆向迭代器,它就会以从尾到头方向来处理序列中的元素。例如:
// 将所有元素逆向拷贝到ite的所指位置上
// rbegin()和rend()与reverse_iterator有关
copy(id.rbegin(), id.rend(), ite);
看似单纯,实现时却大有文章。
首先我们看看rbegin()和rend()。任何容器都提供有这两个操作,我们在本专栏前面的内容中介绍了各种容器时,鲜有列出这两个成员函数,现在举例瞧瞧:
template <class T, class 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 rend() {return reverse_iterator(begin()); }...
};
list和deque中的rbegin()及rend()的定义与vector中的定义极其类似。
没有任何例外!只要双向序列容器提供了begin(), end(),它的rbegin(),rend()就是上面那样的形式。单向序列容器slist不可使用reserve iterators.有些容器如stack,queue,priority_queue并不提供begin()和end(),当然也就没有rbegin(),rend().
现在我们实际看看rbegin()及rend()取出的值分别是什么。代码示例如下:
#include <deque>
#include <iostream>
using namespace std;int main() {int ia[] = {1, 2, 3, 4, 5, 6,7,8};deque<int>id(ia, ia+8);cout << *(id.begin()) << endl; // 1cout << *(id.rbegin()) << endl; // 8cout << *(id.end()) << endl; // 0 dangerouscout << *(id.rend()) << endl; // 0 dangerousreturn 0;
}
为什么“正向迭代器”和“与其相应的逆向迭代器”取出不同的元素呢?这并不是一个潜在的错误,而是刻意为之的特征,主要是为了配合迭代器区间的“前闭后开”习惯。如下图所示,当迭代器逆转时,虽然其实体位置(真正的地址)不变,但其逻辑位置(迭代器所代表的元素)改变了:
唯有这样,才能够保持正向迭代器的一切惯常行为。换句话说,唯有这样,当我们将一个正向迭代器转换为一个逆向迭代器区间后,不必再有任何额外处理,就可以让接受这个逆向迭代器区间的算法,以相反的元素次序来处理区间中的每一个元素。例如:
copy(id.begin(), id.end(), outite); // 1 2 3 4 5 6 7 8cout << endl;copy(id.rbegin(), id.rend(), outite); // 8 7 6 5 4 3 2 1cout << endl;
注意,上述的id.rbegin()是个暂时对象,相当于:
reverse_iterator<deque<int>::iterator>(id.end()); // 指向本例的最后元素
deque<int>::reverse_iterator(id.end()); // 指向本例的最后元素
其中deque::reverse_iterator是一种型别定义,稍早已经展现过。
以下是reverse_iterator的源码:
// 这是一个迭代器适配器,用来将迭代器逆反器逆反前进方向,
// 使前进为后退,后退为前进
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_traits<Iterator>::value_type value_type;typedef typename iterator_traits<Iterator>::difference_type difference_type;typedef typename iterator_traits<Iterator>::pointer pointer;typedef typename iterator_traits<Iterator>::reference reference;typedef Iterator iterator_type; // 代表正向迭代器typedef reverse_iterator self; // 代表逆向迭代器
public:reverse_iterator();explicit reverse_iterator(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--(int) {self tmp = *this;++current;return tmp;}// 前进与后退方向完全逆转self operator+(difference_type n) const {return self(current - n);}self& operator+=(difference_type n) const {current -= n;return *this; }self operator-(difference_type n) const {return self(current + n);}self& operator-=(difference_type n) const {current += n;return *this; }// 注意,下面的第一个*(return后边)和+都会调用本例中的operator*和opertor+// 第二个(this前边)。(判断法则:完全看处理的型别是什么而定)refrence operator[](difference n) const { return *(*this + n); }
};
以下为测试代码
#include <deque>
#include <iostream>
using namespace std;int main() {int ia[] = {1, 2, 3, 4, 5, 6,7,8};deque<int>id(ia, ia+8);ostream_iterator<int> outite(cout, " ");deque<int>::reverse_iterator rite2(id.end());cout << *rite2 << endl; // 8 队尾cout << *(rite2 += 3) << endl; // 5 队尾后退三个位置cout << *(--rite2) << endl; // 6 当前位置前进一个位置 cout << *(rite2.base()) << endl; // 7 回到正向迭代器cout << rite2[3] << endl; // 3 后退三个位置取值return 0;
}
下图显示了上述介个操作队逆向迭代器所造成的移动。
图中[3]的连线之所以为虚线,因为只是取值时迭代器发生了变更,rite2实际上没有发生变更。
stream iterators
所谓stream iterators,可以将迭代器绑定到一个stream对象身上。绑定到istream对象(例如:std::cin)者,称为istream_iterator,称为istream_iterator,拥有输入能力;绑定到ostream对象(例如std::cout)者,称为ostream_iterator,拥有输出能力。两者的用法在之前的例子中都有示范。
所谓绑定一个istream object,其实就是在istream iterator内部维护一个istream memeber,客户端对于这个迭代器所做的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);
protected:istrem* 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;}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.referece operator*() const { return value; }pointer operator->() cosnt { 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::read()函数,等待输入,这不是我们所预期的行为,因此,请在绝对必要的时候才定义所需要的istream iterator—这其实是C++程序员在任何时候都应该遵循的守则,但是很多人都忽略了。
ostream iterator,所谓绑定一个ostream object,就是在其内部维护一个ostream member,客户端对于这个迭代器所做的operator=操作,会被引导调用对应的(迭代器内部所含的)那个ostream member的输出操作(operator<<)。这个迭代器是个OutputIterator。下面是源码:
// 这是一个output iterator,能够将对象格式化输出到某个basic_ostream上
// 注意,此版本为旧有之HP规格,未符合标准接口:ostream_iterator<T, charT, traits>
// 然而一般使用output iterators时都只使用第一个template参数,此时以下仍适用
// 注:SGI STL 3.3已实现出符合标准接口的ostream_iterator。做法与版本大同小异,本版本可读性较高
template <class T>
class ostream_iterator {
protected:ostream* stream;const char* string;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(0) {}ostream_iterator(ostream&s, const char* c) : stream(&s), string(c) { }// 以上ctor的用法// ostream_iterator<int> outiter(cout, " "); 输出到cout,每次间隔一空格ostream_iterator<T> operator*() { return *this; }ostream_iterator<T> operator++() { return *this; }ostream_iterator<T> operator++(int) { return *this; }ostream_iterator<T>& operator=(const T&value) {*stream << value;if (string) *stream << string;return *this;}
};
这两个迭代器,istream_iterator和ostream_iterator,非常重要,这两个迭代器的源码向我们展示了如何为自己量身定制一个迭代器,绑定于我们属意的装置上。正如在本章一开始的概览说明中提到过,以这种迭代器为蓝图稍加修改,便有非常大的应用空间,可以完成一个绑定到Internet Explorer cache身上的迭代器,也可以完成一个绑定到磁盘目录上的迭代器。也可以。。。,泛型世界无限宽广。
function adapters
适配器介绍已经对function adapters做了介绍,并举了一个颇为丰富的运用实例。从这里开始,我们就直接探索SGI STL function adapters的实现细节吧。
一般而言,对于C++ template语法有了某种程度的了解之后,我们很能够理解或想象,容器是以class templates完成,算法以function template完成,仿函数是一种operator()重载class template,迭代器则是一种将operator++和operator*等指针惯常行为重载class template。然后适配器内,应用于容器身上和迭代器身上的适配器,已于本章稍早介绍过,都是一种class template。可应用于仿函数身上的适配器呢?如何能够“事先”对一个函数完成参数的绑定、执行结果的否定、甚至多方函数的组合?请注意我用“事先”一词。我的意思是,最后修饰结果(视为一个表达式,expression)将被给STL算法使用,STL算法才是真正使用这表达式的主体。而我们都知道,只有在真正使用(调用)某个函数(或仿函数)时,才有可能对参数和执行结果做任何干涉。
这是怎么回事?
关键在于,就像本章先前所揭示,container adapters内藏了一个container member一样,或是像revers iterator(adapters)内藏了一个iterator member一样,或是像stream iterator(adapters)内藏了一个pointer to stream一样,或是像insert itertator(adapters)内藏了一个pointer to container(并因而得以取其iterator)一样,每一个function adapters也内藏了一个member object,其型别等同于它所要适配的对象(那个对象当然是一个“可适配的仿函数”,adapter function),下表是一份整理。当function adapter有了完全属于自己的一份修饰对象(的副本)在手,他就成了该修饰对象(的副本)的主人,也就有资格调用该修饰对象(一个仿函数),并在参数和返回值上面进行指定和结果返回了。
辅助函数(helper function) | 实际产生的适配器对象形式 | 内藏成员的模式 |
---|---|---|
bind1st(const Op&op, const T&x) | bind1st<Op>(op, arg1_type(x)) | Op(二元仿函数) |
bind2nd(const Op&op, const T&x) | bind2nd<Op>(op, arg2_type(x)) | Op(二元仿函数) |
not1(const Pred& pred) | unary_negate<Pred>(pred) | Pred放回布尔值的仿函数 |
not2(const Pred& pred) | binary_negate<Pred>(pred) | Pred放回布尔值的仿函数 |
compse1(const Op1&op1, const Op2 & op2) | unary_compose<Op1, Op2>(op1,op2) | Op1,Op2 |
compse2(const Op1&op1, const Op2 & op2), const Op3 &op3 | binary_compose<Op1, Op2, Op3>(op1,op2, op3) | Op1,Op2,Op3 |
ptr_fun(Result(*fp)(Arg)) | pointer_to_unary_function<Arg,Result>(fp) | Result(*fp)(Arg) |
ptr_fun(Result(*fp)(Arg1, Arg2)) | pointer_to_binary_function<Arg1, Arg2,Result>(fp) | Result(*fp)(Arg1, Arg2) |
mem_fun(S (T::*f)()) | mem_func_t<S, T>(f) | S (T::*f)() |
mem_fun(S (T::*f)() const) | const_mem_func_t<S, T>(f) | S (T::*f)() const |
mem_fun_ref(S (T::*f)()) | mem_ref_func_t<S, T>(f) | S (T::*f)() |
mem_fun_ref(S (T::*f)() const) | const_mem_ref_func_t<S, T>(f) | S (T::*f)() const |
mem_fun1(S (T::*f)(A)) | mem_func1_t<S, T, A>(f) | S (T::*f)(A) |
mem_fun1(S (T::*f)(A) const) | const_mem_func1_t<S, T, A>(f) | S (T::*f)(A) const |
mem_fun1_ref(S (T::*f)(A)) | mem_ref_func_t<S, T, A>(f) | S (T::*f)(A) |
mem_fun1_ref(S (T::*f)(A) const) | const_mem_ref_func_t<S, T, A>(f) | S (T::*f)(A) const |
下图鸟瞰了count_if()搭配bind2nd(less(), 12))的例子,其中清楚显示count_if()的是怎么控制函数调用的。
对返回值进行逻辑判断:not1,not2
以下直接列出源码。配合注释进行功能说明。源码中常出现的pred一词,是predicate的缩写,意指会返回真假值(bool)的表达式。
not1源码
// 以下适配器用来表示某个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);}
};// 辅助函数
template <class Predicate>
inline unary_negate<Predicate> not1(const Predicate& pred) {return unary_negate<Predicate>(pred);
}
not2源码
// 以下适配器用来表示某个Adaptable Binary Predicate的逻辑复制(logical negation)
template <class Predicate>
class binary_negate : public binary_function<typename Predicate::first_argument_type,typename Predicate::second_argument_type, 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);}
};// 辅助函数
template <class Predicate>
inline unary_negate<Predicate> not2(const Predicate& pred) {return binary_negate<Predicate>(pred);
}
对参数进行绑定:bind1st, bind2nd
以下是源码:
bind1st源码
// 以下适配器用来将某个Adapter Binary function转换为Unary Function
template <class Operation>
class binder1st : public unary_function<typename Operation::second_argument_type,typename Operation::result_type> {
protected:Operation op;typename Operation::first_argument_type value;
public:binder1st(const Operation& x,const typename ::first_argument_type& y) : op(x), value(y) {}typename Operation::result_typeoperator() (const typename Operation::second_argument_type& x) const {return op(value, x);}
};// 辅助函数,让我们得以方便使用binder1st<Op>
template <class Operation, class T>
inline binder1st<Operation> bind1st(const Operation&op, const T&x) {typedef typename Operation::first_argument_type arg1_type;return binder1st<Operation>(op, arg1_type(x));// 以上,注意,先把x转型为op的第一参数型别
}
bind2nd源码
/ 以下适配器用来将某个Adapter 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:binder1st(const Operation& x,const typename Operation::second_argument_type& y) : op(x), value(y) {}typename Operation::result_typeoperator() (const typename Operation::first_argument_type& x) const {return op(x, 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的第一参数型别
}
用于函数合成:compose1,compose2
以下直接列出源码。注意,本节的两个适配器并未纳入STL标准,SGI STL的私产品,但是颇有钻研价值,我们可以从中学习如何开发适合自己的,更复杂的适配器。
compose1源码
/// 已知两个Adapter Unary Function f(), g(),以下适配器用来产生新的f(),
// 使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:unary_compose(const Operation1& x, const Operation2& y) : op1(x), op2(y) {}typename Operator1::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);
}
compose2源码
// 已知两个Adapter Binary Function f()和两个Adapter Unary Function g1, g2
// 使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:binary_compose(const Operation1& x, const Operation2& y,const Operation3&z) : op1(x), op2(y), op3(z) {}typename Operator1::result_type operator() (const typename Operation2::argument_type& x) const {return op1(op2(x), op3(x));}
};//辅助函数,让我们得以方便运用binary_compose<Op1, Op2, Op3>
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);
}
用于函数指针 ptr_func
这种适配器使我们能够将一般函数当做仿函数使用。一般函数当做仿函数传给STL算法,就语言层面本来就是可以的,就好像原生指针可被当做迭代器传给STL算法那样。但如果不使用这里所说的两个适配器先做一番包装,我们所使用的那个一般函数将不具备适配性,也就无法和前数小节介绍的其他适配器接轨。
以下是源码:
ptr_unary_function
// 以下适配器其实就是把一元函数指针包起来
// 当仿函数被使用时,就调用该函数指针
template <class Arg, class Result>
class pointer_to_unary_function: public unary_function<Arg, Result> {
protected:Result (*ptr)(Arg);
public:pointer_to_unary_function() {}explicit pointer_to_unary_function(Result(*x)(Arg)) : ptr(x) {}Result operator() (Arg x) const { return ptr(x); }
};// 辅助函数,让我们得以方便运用pointer_to_unary_function
inline pointer_to_unary_function<Arg, Result>
ptr_fun(Result(*x)(Arg)) {return pointer_to_unary_function<Arg, Result>(x);
}
ptr_binary_function
/ 以下适配器其实就是把二元函数指针包起来
// 当仿函数被使用时,就调用该函数指针
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() {}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
inline pointer_to_binary_function<Arg1, Arg2, Result>
ptr_fun(Result(*x)(Arg1, Arg2)) {return pointer_to_binary_function<Arg1, Arg2, Result>(x);
}
用于成员函数指针:mem_fun,mem_fun_ref
这种适配器使我们能够将成员函数(member function)当做仿函数来使用,于是成员函数可以搭配各种泛型算法。当容器的元素型别是X&或X*,而我们又以虚拟(virtual)成员函数作为仿函数,便可以藉由泛型算法完成多态调用(polymorphic function call)。这是 泛型(generating)与多态(polymorphism)之间的一个重要接轨。下面是一个实例:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;class Shape
{public: virtual void display() = 0; };class Rect : public Shape
{public: virtual void display() {cout << "Rect ";} };
class Circle : public Shape
{public: virtual void display() {cout << "Circle ";} };
class Square : 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);// polymorphicallyfor (int i = 0; i < v.size(); ++i) (v[i])->display(); // Rect Circle Square Circle Rectcout << endl;// polymorphically and adapterfor_each(v.begin(), v.end(), mem_fun(&Shape::display));cout << endl; // Rect Circle Square Circle Rectreturn 0;
}
注意就语法而言,以下写法都是会导致编译失败的
for_each(v.begin(), v.end(), &Shape::display);
for_each(v.begin(), v.end(), Shape::display);
一定要以适配器mem_fun修饰member function,才能被算法for_each接受。
另一个必须主要的是,虽然多态可以对pointer或reference起作用,但很可惜的是,STL容器只支持“实值语意”,不支持引用语意,因此下面这样无法通过编译:
vector<Shape&> v;
以下是adapters for member function的源码。
// Adapter function objects: pointers to memeber functions.
// 这个族群一共有8个2^3个function objects
// (1) 无任何参数 vs 有一个参数
// (2) 通过pointer vs 通过reference调用
// (3) 通过const成员函数vs no-const成员函数// 无任何参数、通过pointer调用、non-const成员函数
template<class S, class T>
class mem_func_t : public unary_function<T*, S>{
public:explicit mem_func_t(S (T::*pf)()) : f(pf) {}S operator() (T* p) const {return (p->*f)();}
private:S (T::*f)(); //内部成员, pointer to function
};// 无任何参数、通过pointer调用、const成员函数
template<class S, class T>
class const_mem_func_t : public unary_function<T*, S>{
public:explicit const_mem_func_t(S (T::*pf)() const) : f(pf) {}S operator() (T* p) const {return (p->*f)();}
private:S (T::*f)() const; //内部成员, pointer to function
};// 无任何参数、通过reference调用、non-const成员函数
template<class S, class T>
class mem_func_ref_t : public unary_function<T, S>{
public:explicit mem_func_ref_t(S (T::*pf)()) : f(pf) {}S operator() (const T& r) const {return (r.*f)();}
private:S (T::*f)(); //内部成员, pointer to function
};// 无任何参数、通过reference调用、const成员函数
template<class S, class T>
class const_mem_func_ref_t : public unary_function<T, S>{
public:explicit const_mem_func_ref_t(S (T::*pf)() const) : f(pf) {}S operator() (T& r) const {return (r.*f)();}
private:S (T::*f)() const; //内部成员, pointer to function
};// 有一个参数、通过pointer调用、non-const成员函数
template<class S, class T, class A>
class mem_func1_t : public binary_function<T*, A, S>{
public:explicit mem_func1_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 function
};// 有一个参数、通过pointer调用、const成员函数
template<class S, class T, class A>
class const_mem_func1_t : public binary_function<T*, A, S>{
public:explicit const_mem_func1_t(S (T::*pf)(A) const) : f(pf) {}S operator() (T* p, A x) const {return (p->*f)(x);}
private:S (T::*f)(A) const; //内部成员, pointer to function
};// 有一个参数、通过reference调用、non-const成员函数
template<class S, class T, class A>
class mem_func1_ref_t : public binary_function<T, A, S>{
public:explicit mem_func1_ref_t(S (T::*pf)(A)) : f(pf) {}S operator() (const T& r, A x) const {return (r.*f)(x);}
private:S (T::*f)(A); //内部成员, pointer to function
};// 有一个参数、通过reference调用、const成员函数
template<class S, class T, class A>
class const_mem_func1_ref_t : public binary_function<T, S>{
public:explicit const_mem_func1_ref_t(S (T::*pf)(A) const) : f(pf) {}S operator() (T& r, A x) const {return (r.*f)(x);}
private:S (T::*f)(A) const; //内部成员, pointer to function
};
辅助函数
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也的确这么做的
template <class S, class T, class A>
inline mem_fun1_t<S, T, A> mem_fun1(S (T::*f)()) {return mem_fun1_t<S, T, A>(f);
}
template <class S, class T, class A>
inline const_mem_fun1_t<S, T, A> mem_fun1(S (T::*f)() const) {return const_mem_fun1_t<S, T, A>(f);
}template <class S, class T, class A>
inline mem_fun1_ref_t<S, T, A> mem_fun1_ref(S (T::*f)()) {return mem_fun1_ref_t<S, T, A>(f);
}template <class S, class T, class A>
inline const_mem_fun1_ref_t<S, T, A> mem_fun1_ref(S (T::*f)() const) {return const_mem_fun1_ref_t<S, T, A>(f);
}
参考文档1
1:STL源码剖析/侯捷 ↩︎
相关文章:

STL之适配器(adapters)_下
STL之适配器adapters container adapersstackqueue iterator adaptgersinsert iteratorsreverse iteratorsstream iterators function adapters对返回值进行逻辑判断:not1,not2对参数进行绑定:bind1st, bind2nd用于函数合成:compose1,compose2用于函数指针 ptr_func…...

基于51单片机64位病床呼叫系统设计( proteus仿真+程序+设计报告+原理图+讲解视频)
基于51单片机病床呼叫系统设计( proteus仿真程序设计报告原理图讲解视频) 仿真图proteus7.8及以上 程序编译器:keil 4/keil 5 编程语言:C语言 设计编号:S0095 1. 主要功能: 基于51单片机的病床呼叫系统proteus仿…...

安装 Zookeeper 和 Kafka
注意:需要java环境 [roothcss-ecs-2a6a ~]# java -version java version "17.0.12" 2024-07-16 LTS Java(TM) SE Runtime Environment (build 17.0.128-LTS-286) Java HotSpot(TM) 64-Bit Server VM (build 17.0.128-LTS-286, mixed mode, sharing) [roo…...

操作系统输入输出系统知识点
I/O系统的功能、模型和接口 I/O系统的基本功能 隐藏物理设备的细节与设备的无关性提高处理机和I/O设备的利用率对1/0 设备进行控制确保对设备的正确共享 独占设备,进程应互斥地访问这些设备共享设备,在一段时间内允许多个进程同时访问的设备 错误处理 I…...

C语言控制语句与案例
控制语句与案例 1. 选择结构 1.1 if 语句 if 语句用于根据条件执行不同的代码块。最基本的语法形式如下: // 单分支 if (条件) {// 条件为真时执行的代码 }// 双分支 if (条件) {// 条件为真时执行的代码 } else {// 条件为假时执行的代码 }// 多分支 if (条件1…...

JVM的内存布局
Java虚拟机(JVM)的内存布局可以分为几个主要部分,每个部分都有特定的用途。以下是JVM内存布局的基本组成: 方法区(Method Area): 方法区是所有线程共享的内存区域,用于存储已被虚拟机…...

aws codepipeline + github + sonarqube + jenkins实践CI/CD
https://blog.csdn.net/u011564831/article/details/144007981文章浏览阅读1.2k次,点赞31次,收藏21次。本文使用 Jenkins 结合 CodeBuild, CodeDeploy 实现 Serverless 的 CI/CD 工作流,用于自动化发布已经部署 lambda 函数。在 AWS 海外区&a…...

mistralai 部署笔记
目录 mistralai 部署笔记 mistralai 部署笔记 #! /usr/bin/env python3 import os import sys import torch os.chdir(os.path.dirname(os.path.abspath(__file__)))current_dir = os.path.dirname(os.path.abspath(__file__))paths = [os.path.abspath(__file__).split(scri…...

Java——异常机制(上)
1 异常机制本质 (异常在Java里面是对象) (抛出异常:执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给JRE) 工作中,程序遇到的情况不可能完美。比如…...
坐标系,向量_batch及向量点乘部分知识
坐标系 Unity所采用的是左手坐标系。 对于Vector3.forward ,其坐标值为(0,0,1),为定值 而transform.forward 该值不固定,本地坐标正方向所在世界坐标系中的方向 向量 向量是终点位置减去起始点位置得…...

【计算机网络】期末速成(2)
部分内容来源于网络,侵删~ 第五章 传输层 概述 传输层提供进程和进程之间的逻辑通信,靠**套接字Socket(主机IP地址,端口号)**找到应用进程。 传输层会对收到的报文进行差错检测。 比特流(物理层)-> 数据帧(数据链路层) -> 分组 / I…...

【设计模式】结构型设计模式总结之代理模式、装饰模式、外观模式、享元模式
文章目录 代理模式示例结构分类动态代理 装饰模式示例结构使用场景与代理模式区别Context 外观模式结构示例使用场景Context 享元模式结构示例使用场景Message 代理模式 代理模式(Proxy Pattern) 是一种结构型设计模式,它提供了一个代理对象…...

11进阶篇:专业课论文阅读方向指南(2025版)
文章目录 第一个检索式:图情档核心期刊(北大 + CSSCI)发文情况研究方法类关键词研究主题类关键词论文阅读建议第二个检索式:川大公共管理学院在核心期刊(北大 + CSSCI)的发文情况研究方法类关键词研究主题类关键词特点关键词与2024年972(现815)两道题目的映射情况815信…...

watch里可以写异步吗
在Vue的 watch 中可以写异步,但通常不推荐。 原因 - 可维护性差: watch 的主要用途是响应式地监听数据变化。如果在里面写复杂的异步操作,会让代码逻辑变得难以理解和维护。例如,同时监听多个数据变化并触发不同异步操作时&am…...

基于 Spring Boot + Vue 的宠物领养系统设计与实现
引言 近年来,随着人们生活水平的提高,宠物逐渐成为许多家庭的重要成员。然而,宠物的流浪和弃养问题日益严重,这促使社会对宠物领养的需求不断增长。为解决宠物领养中信息不对称、领养流程复杂等问题,设计并实现一个基…...

leetcode399:除法求值
给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。 另有一些以数组 queries 表示的问题,其中 queries[j]…...

【10】MySQL中的加密功能:如何使用MD5加密算法进行数据加密
文章目录 1. MySQL加密功能概述2. MD5加密算法3. 在MySQL中使用MD5加密4. 使用更安全的加密方法总结 在现代的数据库应用中,数据的安全性和隐私性变得尤为重要。无论是存储用户的个人信息,还是保护敏感的业务数据,确保这些数据不会被未授权访…...

CSS的2D和3D动画效果
CSS的2D和3D动画效果:网页动态设计的魔法 在现代网页设计中,动画已经成为提升用户体验的重要元素。通过引入动态效果,我们不仅可以使交互更加流畅和直观,还能吸引用户的注意力,增强品牌认知度。CSS提供了强大的工具&a…...

30天学会Go--第9天 GO语言 Mysql 学习与实践
30天学会Go–第9天 GO语言 MySQL学习与实践 文章目录 30天学会Go--第9天 GO语言 MySQL学习与实践前言一、MySQL 基础知识1.1 MySQL 的核心特征1.2 MySQL 的常见使用情景 二、安装 MySQL2.1 Windows 安装2.2 macOS 安装2.3 Linux 安装 三、MySQL 常用命令3.1 数据库操作3.2 表操…...

跟李笑来学美式俚语(Most Common American Idioms): Part 54
Most Common American Idioms: Part 54 前言 本文是学习李笑来的Most Common American Idioms这本书的学习笔记,自用。 Github仓库链接:https://github.com/xiaolai/most-common-american-idioms 使用方法: 直接下载下来(或者clone到本地…...

Angular由一个bug说起之十一:排序之后无法展开 Row
问题现象 在使用 Material Table 时,排序功能触发了一个奇怪的 Bug:表格的 Row 无法展开。最终排查发现,问题的根源在于 trackBy 的错误使用。trackBy 方法接受两个参数:index(数据索引)和 row(…...

使用 Flutter 进行移动应用开发:深入探索
文章目录 前言一、介绍二、安装 Flutter 环境三、Flutter 应用结构与基础组件四、状态管理策略五、高级主题结语 前言 随着移动技术的迅猛发展,跨平台开发的需求日益增长。开发者们一直在寻找一种既能保证应用性能又能减少开发成本和时间的技术方案。Flutter 应运而…...

2024年天津市职业院校技能大赛高职组 “信息安全管理与评估”样题第三阶段
(四)第三阶段竞小组(赛项)目(300分) 第三阶段竞赛内容是:网络安全渗透(夺旗挑战赛CTF) 本模块要求参赛者作为攻击方,运用所学的信息收集、漏洞发现、漏洞利用等渗透测试技…...

docker批量创建cloudstack虚拟主机脚本
批量创建cloudstack脚本 #!/bin/bash # 配置变量 container_prefix"cloudworker-" base_ip"192.168.1." start_ip2 #开始ip start_container2 #上同 end_container4 #结束ip 包括 network_name"my_macvlan_network" image_name"dockedahi:…...

npm发布插件到私有仓库保姆级教程
在开发项目的过程中,我们经常需要安装插件依赖,那么怎么把自己开发的组件封装成一个插件,并发布到npm 插件市场或者上传到私有仓库里面呢?今天总结下自己发布插件到私有仓库的记录: 一、创建组件 执行命令创建一个空…...

WinRAR V7.10纯净体验
前言 很多同学在安装了WinRAR之后,每次用这个软件解压文件时,都会先跳出一个广。这个广就像打开了一个新窗口,很打扰人。从WinRAR的5.40版本开始,哪怕是简体中文版的,都会这样弹广告。不管你有没有注册账号࿰…...

scss文件内引入其他scss文件报错
1、今天在编译一些老项目的时候,老是提示下面信息 2、而且有很多Sass import rules are deprecated and will be removed in Dart Sass 3.0.0.警告 3、用npm view sass versions看,其中sass的最新版本是1.82.0 4、经过测试"sass": "1.75…...

1-12 GD32基于定时器输入捕获
前言: 基于本人对相关知识回顾与思考,仅供学习参考 目录 前言: 1.0 输入捕获 2.0 信号周期 3.0 定时器配置 4.0 定时器配置 5.0 定时器中断 后记: 1.0 输入捕获 2.0 信号周期 获取信号周期的方法,在第一次捕获与…...

前端基础的讲解-JS(22)
什么是JSON? 1.json 是一种轻量级的数据交换格式 简单来说:json 就是一种在各个编程语言中流通的数据格式,负责不同编程语言中的数据传递和交互。 类似于: 国际通用语言 - 英语 中国 56 个民族不同地区的通用语言 - 普通话 …...

Minecraft-Datapack数据包开发3-进度与成就
目录 简介成就与进度根进度叶子进度更多的检测方式 简介 代码已经上传: gitee github 成就与进度 工欲善其事必先利其器,别死记硬背,多使用自动生成网站 进度数据包生成器:https://misode.github.io/advancement/指令生成器&…...