本文主要是介绍【侯捷】C++STL标准库与泛型编程(第三讲),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
第三讲
算法的形式
- C++标准库的算法,是什么东西?
说明:
-
算法Algorithm 是个 function template,标准库中的算法都长成如下这样:
template<typename Iterator> Algorithm(Iterator itr1, Iterator itr2) {... }template<typename Iterator, typename Cmp> Algorithm(Iterator itr1, Iterator itr2, Cmp comp) {... }
-
算法看不见容器,对其一无所知;所以,它所需要的一切信息都必须从Iterators取得,而Iterators(由Containers供应)必须能够回答Algorithm的所有提问,才能搭配该 Algorithm 的所有操作;
-
如果算法提问,迭代器无法回答,那么编译到那一行就会报错;
迭代器的分类
各种容器的iterators
的iterator_category
说明:
- 迭代器是由容器提供的
Array
和Vector
都是连续空间,所以它们的迭代器应该是Random_Access
;Deque
是分段连续的,是假的连续,但是给使用者的感觉是真的连续,所以它的迭代器也应该是Random_Access
;list
是双向链表,空间不是连续的,所以它的迭代器是bidirection
, 可以往前,也可以往后,但是不能跳跃;forward_list
是单向链表,所以它的迭代器是forward_iterator
;set
/map
/multiset
/multimap
底层结构都是红黑树,红黑节点之间是双向的,所以它们的迭代器都是bidirection
这种类型;unordered
系列的容器底层是Hashtable
,每个bucket下是一个链表,所以要看这个链表是单向还是双向的,才能决定hashtable是forward
还是bidirection
类型;
- 容器提供的迭代器的分类不是1,2,3,4,5这样的整数,而是如上所示的这种对象。五种iterator category,都是类,且存在继承关系:
input_iterator_tag
forward_iterator_tag
bidirectional_iterator_tag
random_access_iterator_tag
output_iterator_tag
- 如下程序打印出各种容器的迭代器的分类的形式:
程序说明:
- 传入各种容器的迭代器给
dispaly_category(I itr)
函数,其中类似array<int,10>::iterator()
的形式是个临时对象,没有名称; iterator_traits<I>::iterator_category
就是向iterator_traits
提问迭代器的类型;display_category(xx_iterator_tag)
是重载函数,这里就说明了为什么分类不用整数,而是用对象来表示,因为category不论是什么,往上调用display_category
函数自然而然编译器就会找到是哪个,如果是整数,就无法写得这么漂亮;
#include <iostream>
#include <array>
#include <vector>
#include <list>
#include <forward_list>
#include <deque>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <iterator>
using namespace std;void display_category(random_access_iterator_tag) { cout << "random_access_iterator" << endl; }
void display_category(bidirectional_iterator_tag) { cout << "bidirectional_iterator" << endl; }
void display_category(forward_iterator_tag) { cout << "bidirectional_iterator" << endl; }
void display_category(output_iterator_tag) { cout << "output_iterator" << endl; }
void display_category(input_iterator_tag) { cout << "input_iterator" << endl; }template <typename I>
void display_category(I itr) {typename iterator_traits<I>::iterator_category cagy;display_category(cagy);
}int main() {cout << "\ntest_iterator_category().....\n";display_category(array<int, 10>::iterator());display_category(vector<int>::iterator());display_category(list<int>::iterator());display_category(forward_list<int>::iterator());display_category(deque<int>::iterator());display_category(set<int>::iterator());display_category(map<int, int>::iterator());display_category(multiset<int>::iterator());display_category(multimap<int, int>::iterator());display_category(unordered_set<int>::iterator());display_category(unordered_map<int, int>::iterator());display_category(unordered_multiset<int>::iterator());display_category(unordered_multimap<int, int>::iterator());//istream_iterator和ostream_iterator在iterator头文件中display_category(istream_iterator<int>());display_category(ostream_iterator<int>(cout, ""));return 0;
}
#linux实测运行结果
test_iterator_category().....
random_access_iterator
random_access_iterator
bidirectional_iterator
bidirectional_iterator
random_access_iterator
bidirectional_iterator
bidirectional_iterator
bidirectional_iterator
bidirectional_iterator
bidirectional_iterator
bidirectional_iterator
bidirectional_iterator
bidirectional_iterator
input_iterator
output_iterator
各种容器的iterators
的iterator_category
的typeid
上面的程序是人为进行了些加工,指定了要打印的字符串,但是通过typeid
可以直接打印出iterator_category
#include <iostream>
#include <array>
#include <vector>
#include <list>
#include <forward_list>
#include <deque>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <iterator>
#include <typeinfo>
using namespace std;void display_category(random_access_iterator_tag) { cout << "random_access_iterator" << endl; }
void display_category(bidirectional_iterator_tag) { cout << "bidirectional_iterator" << endl; }
void display_category(forward_iterator_tag) { cout << "bidirectional_iterator" << endl; }
void display_category(output_iterator_tag) { cout << "output_iterator" << endl; }
void display_category(input_iterator_tag) { cout << "input_iterator" << endl; }template <typename I>
void display_category(I itr) {typename iterator_traits<I>::iterator_category cagy;display_category(cagy);cout << "typeid(itr).name() = " << typeid(itr).name() << endl << endl;//The output depends on library implementation//The particular representation pointed by the//returned values is implementation-defined, 返回值由实现的版本定义//and may or may not be different for different types.
}int main() {cout << "\ntest_iterator_category().....\n";display_category(array<int, 10>::iterator());display_category(vector<int>::iterator());display_category(list<int>::iterator());display_category(forward_list<int>::iterator());display_category(deque<int>::iterator());display_category(set<int>::iterator());display_category(map<int, int>::iterator());display_category(multiset<int>::iterator());display_category(multimap<int, int>::iterator());display_category(unordered_set<int>::iterator());display_category(unordered_map<int, int>::iterator());display_category(unordered_multiset<int>::iterator());display_category(unordered_multimap<int, int>::iterator());display_category(istream_iterator<int>());display_category(ostream_iterator<int>(cout, ""));return 0;
}
运行结果:
test_iterator_category().....
random_access_iterator
typeid(itr).name() = Pi random_access_iterator
typeid(itr).name() = N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE bidirectional_iterator
typeid(itr).name() = St14_List_iteratorIiE # 编译器在编译的时候会在名称前后添加一些东西bidirectional_iterator
typeid(itr).name() = St18_Fwd_list_iteratorIiErandom_access_iterator
typeid(itr).name() = St15_Deque_iteratorIiRiPiEbidirectional_iterator
typeid(itr).name() = St23_Rb_tree_const_iteratorIiEbidirectional_iterator
typeid(itr).name() = St17_Rb_tree_iteratorISt4pairIKiiEEbidirectional_iterator
typeid(itr).name() = St23_Rb_tree_const_iteratorIiEbidirectional_iterator
typeid(itr).name() = St17_Rb_tree_iteratorISt4pairIKiiEEbidirectional_iterator
typeid(itr).name() = NSt8__detail14_Node_iteratorIiLb1ELb0EEEbidirectional_iterator
typeid(itr).name() = NSt8__detail14_Node_iteratorISt4pairIKiiELb0ELb0EEEbidirectional_iterator
typeid(itr).name() = NSt8__detail14_Node_iteratorIiLb1ELb0EEEbidirectional_iterator
typeid(itr).name() = NSt8__detail14_Node_iteratorISt4pairIKiiELb0ELb0EEEinput_iterator
typeid(itr).name() = St16istream_iteratorIicSt11char_traitsIcElEoutput_iterator
typeid(itr).name() = St16ostream_iteratorIicSt11char_traitsIcEE
istream_iterator的iterator_category
说明:
- 接口必须相同,实现可以不同
- G2.9版本的
istream_iterator
有两个模板参数,G3.3版本有4个模板参数,但是后面两个都有默认值;G4.9版本也是4个模板参数; - 各个版本都定义了自己的
iterator_category
为input_iterator_tag
;
ostream_iterator的iterator_category
说明:
- 和
istream_iterator
类似,各个版本都是在定义自己的iterator_category
为output_iterator_tag
;
迭代器分类对算法的影响
distance
算法
说明:
distance
算法是计算两个指针之间的距离:- 如果是连续空间,可以直接相减,调用
__distance(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag)
函数; - 如果不是连续空间,则将
first
一直往后走直到和last
重合,这个往后走的步数,就是两个迭代器之间的距离,调用的是__distance(InputIterator first, InputIterator last, input_iterator_tag)
函数;
- 如果是连续空间,可以直接相减,调用
distance
算法的返回值要通过询问iteartor_traits
获得difference_type
,传入的Iterator
的category
也要通过询问iterator_traits
获得;category()
是个临时对象,记住这种写法typename()
是创建临时对象;- 如果两个迭代器之间有100万个元素,那么这两个
__distance
函数的效率截然不同;
advance
算法
说明:
advance
算法,根据iterator_category
的不同调用不同的__advance
函数;iterator_category(const Iterator&)
函数就是将传入的迭代器参数丢给iterator_traits
,询问其对应的iterator_category
是什么,即如图中的解释:协助取出iterator
的category
并以此创建一个临时对象;- 迭代器的分类之所以没有使用整数来表示,而是使用对象,是因为这些分类之间有继承关系,如果问出来的分类是
forward_iterator_tag
,代码中并没有专门为其设计的版本,因为forward_iterator_tag
是input_iterator_tag
的子类,那么就会进入input_iteartor_tag
的版本;
copy
算法
说明:
copy
算法就是复制,需要知道来源端的起点、终点以及目的端的起点;copy
中不断地在检查它所接收到的迭代器是不是特属于某种类型而决定是否要做特别的动作来加快速度;- 如果迭代器
first
和last
是const char*
类型,则调用memmove()
,直接传到目的端; - 如果迭代器是
const wchar_t*
类型,也是直接做memmove()
; - 如果迭代器是
InputIterator
类型,则调用__copy_dispatch()
;在__copy_dispatch()
函数中又去检查迭代器的类型:- 如果是指针类型,则调用
__copy_t()
函数,判断是否有重要的拷贝构造函数,如果有不重要的拷贝构造函数,就调用memmove()
函数;如果有重要的拷贝构造函数,就调用__copy_d()
函数,用尾减头确定要拷贝的元素个数,减少了循环次数,速度较快; - 如果是
<InputIterator,InputIterator>
类型,调用__copy()
函数,并在该函数中继续判断迭代器的类型:- 如果是
InputIterator
类型,则从头开始一个个拷贝,直到拷贝到尾,速度很慢; - 如果是
RandomAccessIterator
类型,则调用__copy_d()
函数,用尾减头确定要拷贝的元素个数,减少了循环次数,速度较快;
- 如果是
- 如果是指针类型,则调用
- 如果迭代器
- 图中涉及到的
Type Traits
,将在第四讲中讲解;
destroy
算法
说明:
- 和前面讲解的算法一样,根据传入的迭代器的类型做不同的操作,以使得速度变快;
- 如果要销毁的元素的析构函数不重要,则什么都不做;否则,调用析构函数;
unique_copy
算法
算法源码中对iterator_category的”暗示“
算法是模板函数,即可以接受各种类型。
说明:
sort
算法的typename命名为RandomAccessIterator
就是暗示接受的迭代器为RandomAccessIterator
,此处的这个typename可以命名为其他的,之所以命名为当前这个,就是一种”暗示“;distance
算法暗示接受的迭代器是InputIterator
都可以;find
算法暗示接受InputIterator
;rotate
算法暗示接受ForwardIterator
;reverse_copy
算法暗示接受BidirectionalIterator
;
算法源码剖析
算法要满足模板函数的类型才是C++标准库提供的算法。qsort
和bsearch
算法是C中的函数,不是标准库中提供的算法。而count_if
、find
和sort
算法是C++标准库中提供的算法。
算法accumulate
说明:
-
accumulate
算法的功能是累计,图上有两个版本:- 三个参数的情况,进行累加,初值加上每个元素;
- 四个参数的情况,每次都将初值和元素进行
binary_op
的操作;
-
测试代码:
#include <iostream> // std::cout #include <functional> // std::minus #include <numeric> // std::accumulate namespace jj34 { int myfunc (int x, int y) {return x+2*y;}struct myclass {int operator()(int x, int y) {return x+3*y;} } myobj;void test_accumulate() {cout << "\ntest_accumulate().......... \n"; int init = 100;int nums[] = {10,20,30};cout << "using default accumulate: ";cout << accumulate(nums,nums+3,init); //160 使用指针作为迭代器,且前闭后开cout << '\n';cout << "using functional's minus: ";//minus<int>是C++标准库提供的functor,减法cout << accumulate(nums, nums+3, init, minus<int>()); //40 cout << '\n';cout << "using custom function: ";//myfunc就是个函数cout << accumulate(nums, nums+3, init, myfunc); //220 cout << '\n';cout << "using custom object: ";//myobj是函数对象(function object/functor)/仿函数,是个类或结构体,重载()cout << accumulate(nums, nums+3, init, myobj); //280cout << '\n'; } }
算法for_each
说明:
-
for_each
算法对first
到last
范围的每个元素做f
操作,参数f
可以是任何东西,只要能被()
调用起来; -
测试代码:
#include <iostream> // std::cout #include <algorithm> // std::for_each #include <vector> // std::vector namespace jj35 { void myfunc (int i) { //functioncout << ' ' << i; }struct myclass { //function objectvoid operator() (int i) { cout << ' ' << i; } } myobj;void test_for_each() {cout << "\ntest_for_each().......... \n"; vector<int> myvec;myvec.push_back(10);myvec.push_back(20);myvec.push_back(30);for_each (myvec.begin(), myvec.end(), myfunc);cout << endl; //output: 10 20 30for_each (myvec.begin(), myvec.end(), myobj);cout << endl; //output: 10 20 30//since C++11, range-based for- statementfor (auto& elem : myvec)elem += 5;for (auto elem : myvec)cout << ' ' << elem ; //output: 15 25 35 } }
-
C++11开始新的
for
循环形式:for (decl : coll) {statement }
算法replace,replace_if,replace_copy
说明:
replace
函数的参数:头、尾、旧值、新值;条件是元素值和旧值相同;replace_if
函数表示需要给定一个条件,参数:头、尾、条件、新值;符合pred(*first)
作为条件;replace_copy
函数功能是将旧区间的等于旧值的都以新值的形式放到新区间中;
算法count,count_if
说明:
- 左边是标准库的算法,全局的,泛化的;右边是说明容器中是否有该同名的成员函数;
- 容器不带成员函数
count()
:array
vector
list
forward_list
deque
- 容器带有成员函数
count()
: 使用的时候就要使用容器中的count
,针对这种类型的容器做了特别的处理set/multiset
map/multimap
unordered_set/unordered_multiset
unordered_map/unordered_multimap
- 容器带有成员函数
count()
的这8个,是associated container,可以用key找到data,小型的数据库,有严谨的结构,内部使用红黑树或哈希表实现,有自己比较快的计数方式;
算法find,find_if
说明:
find
查找,循序查找;find_if
查找符合条件的元素;set/multiset
、map/multimap
、unordered_set/unordered_multiset
和unordered_map/unordered_multimap
因为有自己严谨的结构,所以使用这些结构,不需要调用全局的find
;
算法sort
说明:
-
set/multiset
、map/multimap
、unordered_set/unordered_multiset
和unordered_map/unordered_multimap
不带sort
,因为遍历自然就形成了sorted
状态; -
默认的排序方法也是从小到大排序;
-
测试代码:
#include <iostream> // std::cout #include <algorithm> // std::sort #include <vector> // std::vector namespace jj36 { bool myfunc (int i,int j) { return (i<j); }struct myclass {bool operator() (int i,int j) { return (i<j);} } myobj;bool test_sort() { cout << "\ntest_sort().......... \n"; int myints[] = {32,71,12,45,26,80,53,33};vector<int> myvec(myints, myints+8); // 32 71 12 45 26 80 53 33// using default comparison (operator <):sort(myvec.begin(), myvec.begin()+4); //(12 32 45 71)26 80 53 33// using function as compsort(myvec.begin()+4, myvec.end(), myfunc); // 12 32 45 71(26 33 53 80)// using object as compsort(myvec.begin(), myvec.end(), myobj); //(12 26 32 33 45 53 71 80)// print out content:cout << "\nmyvec contains:";for (auto elem : myvec) //C++11 range-based for statementcout << ' ' << elem ; //output: 12 26 32 33 45 53 71 80// using reverse iterators and default comparison (operator <):sort(myvec.rbegin(), myvec.rend()); // print out content:cout << "\nmyvec contains:";for (auto elem : myvec) //C++11 range-based for statementcout << ' ' << elem ; //output: 80 71 53 45 33 32 26 12 // using explicitly default comparison (operator <):sort(myvec.begin(), myvec.end(), less<int>()); // print out content:cout << "\nmyvec contains:";for (auto elem : myvec) //C++11 range-based for statementcout << ' ' << elem ; //output: 12 26 32 33 45 53 71 80 // using another comparision criteria (operator >):sort(myvec.begin(), myvec.end(), greater<int>()); // print out content:cout << "\nmyvec contains:";for (auto elem : myvec) //C++11 range-based for statementcout << ' ' << elem ; //output: 80 71 53 45 33 32 26 12 } }
-
list
和forward_list
中带有成员函数sort()
,因为全局的sort
要求是RandomAccessIterator
,可以跳跃,但是list
和forward_list
容器都不能跳跃;
关于reverse iterator,rbegin(),rend()
说明:
- 图中的绿色小箭头表示如果对迭代器取值得到的元素;
rbegin()
对应的就是end()
的指针,rend()
对应的就是begin()
的指针,但是需要改变它们的行为,所以加了一个iterator adapter——reverse_iterator
,使得取值的时候和原来的指针取值方式变得不同;
算法binary_search
说明:
binary_search
算法一定是在排序的序列中使用;binary_search
就是通过调用lower_bound
函数来完成查找;lower_bound
找到的是在不违反排序的情况下,找到的能安插要查找的数的最低点,upper_bound
是最高点,即返回的是能安插的位置;
仿函数和函数对象
仿函数functors
说明:
- 仿函数只为算法服务;
- 仿函数就是要模仿函数,必须重载
()
,图中是标准库提供的三大分类的functor; - 之所以要将这些操作设计成函数,是为了将这些动操作传到算法中,所以要写成函数或仿函数;
- 每个标准库提供的仿函数都继承了
binary_function
;
说明:
identity
/select1st
/select2nd
是GNU C++独有的,C++标准库的规格里并没有规定必须有这些;- G4.9中这三个仿函数的名称变了,变成了
_Identity
/_Select1st
/_Select2nd
;
说明:
-
// using explicitly default comparison (operator <):sort(myvec.begin(), myvec.end(), less<int>());
和
sort(myvec.begin(), myvec.end());
是一样的,从小到大排序,只是将默认的比较方法写出来了,less<int>
是类型,less<int>()
产生了一个临时对象; -
// using another comparision criteria (operator >):sort(myvec.begin(), myvec.end(), greater<int>());
使用
greater<int>()
就变成了逆向排序,从大到小进行排序; -
此处自己编写的仿函数
struct myclass
没有继承binary_function
,就没有融入STL中,那么未来就没有被改造的机会;如果希望自己写的functor能够和STL的体系结构如何的话,就必须选择”适当者“(unary_function
或binary_function
)继承。。
仿函数functors的可适配(adaptable)条件
说明:
binary_function
就是两个操作数的操作(+,-,…),unary_function
就是一个操作数;binary_function
和unary_function
的相同点是接受模板参数并将参数换了个名称,没有data,没有function,所以它们的大小都是0(理论值,实际上可能是1),如果作为父类,那它的大小一定是0;- STL规定每个Adaptable Function都应挑选适当者继承之(因为Function Adapter将会提问)。所谓的"可适配(adaptable)"的意思就是可以被修改、适配或调整。如果你希望你写的functor是适配的,那么就要挑选一个”适当者“继承,因为当你被Adapter改造的时候,Adapter可能会来问这些问题,类似算法问迭代器问题。例如
less
是两个操作的数的操作,所以它要继承binary_function
,这里只是继承了typedef,并没有增加额外的开销(没有数据和方法)。 - 仿函数如果要可适配的条件是能Adapter回答问题,为了能回答Adapter的问题就必须继承”适当者“;
- 小结:仿函数就是一个类中重载了
()
,这样的类创建出来的对象就叫做函数对象或者仿函数,之所以叫做函数对象,是因为创建出来的对象是一个对象,但是它像一个函数;
存在多种Adapter
说明:
- 根据Adapter需要修饰或改造的东西的名称则将其命名为什么:
Function Adapters
:改造Functor;Iterator Adapters
: 改造Iterator;Container Adapters
: 改造Container;
- 所有的Adapter都是用复合而非继承的方式来使用所要改造的东西的功能;
- 回顾:算法不知道容器,能够看到的只有迭代器,而迭代器是由容器提供,所以算法想要知道的一切问题都要通过5种typedef来询问迭代器,所以迭代器必须有这5种type;
- Adapter是仿函数和算法之间的桥梁,Function Adapter可能想要知道Functors的一些特性,也是通过问和答来实现的,通过3或2个typedef来询问;
容器Adapter
说明:
stack
和queue
都内含了一个Sequence
,默认是个deque
,隐藏了某些deque
的操作,这是一种改造,修改了函数名称也是一种改造:- stack:
push_back
->push
;pop_back
->pop
; - queue:
push_back
->push
;pop_front
->pop
;
- stack:
函数Adapter
函数适配器:binder2nd
说明:
-
binder2nd
类中的typename Operation::second_argument_type value;
就是在提问:binder2nd
这个Adapter在修饰改造一个动作Operation
(如之前的less
),询问Operation
的second_argument_type
是什么,就是Adapter向Functor提问,Functor要回答; -
count_if(vi.begin(), vi.end(), not1(bind2nd(less<int>(), 40)));//bind2nd是将第二个参数绑定为特定值,此处是40//less<int>():less<int>是类型,加上()产生了临时对象,并不是调用 要准确区分()是创建对象还是函数调用
-
bind2nd
实际上调用了binder2nd
; -
binder2nd
的op
会记录less<int>()
,value
会记录40,修饰functor,最后也要表现为functor,所以重载operator()
,修饰完成后传给count_if
; -
调用流程就是:
count_if
->bind2nd
->binder2nd
->operator()
; -
辅助函数
bind2nd
的作用是根据实参推导出Opeartion
的类型; -
binder2nd<Operation>(op, arg2_type(x))
也是一个临时对象,其中binder2nd<Operation>
是类型,而创建临时对象会调用binder2nd
的构造函数;这个对象作为第三参数传入count_if
,即pred
这个参数,然后pred(*first)
就会调用operator()
; -
函数对象(图中是
less<int>()
)必须能够回答second_argument_type
、first_argument_type
和result_type
才能和binder2nd
这个Adapter完美搭配使用,如果能回答则说这个functor是可适配(adaptable)的,可适配的条件就是要继承binary_function
; -
typename
的作用是帮助编译器通过编译,在编译的时候,还没有被使用,所以Operation
是什么不知道,因此也不知道Operation::second_argument_type
是什么,它到底是什么东西,将来是否会出错,都是编译器会考虑的,所以编译器会犹豫不决,正常来讲编译到这里的时候不应该通过编译,而加了typename
就是坚定编译器的决心,这是个问答形式,告诉编译器Operation::second_argument_type
就是个typename,让它编译通过吧;这种很长的提问前面一般都会加typename; -
如果binder2nd这个functor要可适配的话,就要继承
unary_function
,而它的操作数就是bind2nd
这个结果;
函数适配器:not1
说明:
not1(bind2nd(less<int>(), 40))
就是不小于40的数;bind2nd(less<int>(), 40)
会作为实参传入not1
函数中,函数模板会进行实参推导,推导出Predicate
是什么类型,推导出来之后创建临时对象,即调用unary_negate
类的构造函数;
新型适配器,bind
说明:
-
右侧的已经过时了,现在使用左侧的替代;
-
如下是bind的使用示例:
-
bind
是从C++11开始提供的适配器,可以绑定:- 函数
- 函数对象
- 成员函数:_1必须是某个object地址,其中的
_1
是占位符(placeholder) - 数据成员:_1必须是某个object地址
-
//binding functions:auto fn_five = bind(my_divide, 10, 2); //绑定my_divide函数,绑定它的两个参数为10和2auto fn_half = bind(my_divide, _1, 2); //绑定my_divide函数第二参数为2,第一参数留着cout << fn_half(10) << '\n'; //因为只绑定了一个参数,所以调用的时候还需要传入一个参数,传入的参数落在_1位置上auto fn_invert = bind(my_divide, _2, _1); //绑定my_divide函数,两个参数都空置cout << fn_invert(10, 2) << '\n'; //10是第一实参,会落在_1的位置上;2是第二实参,会落在_2这个位置上;auto fn_rounding = bind<int>(my_divide, _1, _2); //指定了模板参数,就改变return type为intcout << fn_rounding(10,3) << '\n';
-
//binding members:MyPair ten_two{10,2}; //设定对象并给定初值//成员函数其实有个参数:thisauto bound_memfn = bind(&MyPair::multiply, _1); //绑定MyPair的成员函数multiply,_1空闲,占位this参数cout << bound_memfn(ten_two) << '\n'; //调用的时候指定auto bound_memdata = bind(&MyPair::a, ten_two); //调用的时候绑定了this参数cout << bound_memdata() << '\n'; //取得aauto bound_memdata2 = bind(&MyPair::b, _1); //调用的时候绑定了this参数cout << bound_memdata2(ten_two) << '\n'; //取得b
-
vector<int> v{15, 37, 94, 50, 73, 58, 28, 98};//cbegin()表示返回的迭代器是不可以更改的,c是const的意思int n = count_if(v.cbegin(), v.cend(), not1(bind2nd(less<int>(), 50)));//用bind替换bind2ndauto fn_ = bind(less<int>(), _1, 50);cout << count_if(v.cbegin(), v.cend(), fn_) << endl;cout << count_if(v.begin(), v.end(), bind(less<int>(), _1, 50)) << endl;
迭代器Adapter
迭代器适配器:reverse_iterator
说明:
reverse_iterator
的关键就是operator*
,对逆向迭代器取值,就是对「对应的正向迭代器」退一位取值;- 注意其他的操作,都是和正向迭代器的操作是相反的;
迭代器适配器:inserter
说明:
copy
操作是将源端的数据拷贝到目的端,first
和last
分别是源端的迭代器的头和尾,result
是目的端的起始位置,所以将数据拷贝到目的端的时候,目的端的空间是否足够、是否合法,copy
操作是不知道的,所以copy
和一般的迭代器的搭配的时候拷贝动作是进行”赋值“,有隐患;- 我们希望在要插入的位置自己准备空间,放一个元素则创建一个新的空间,而不是进行”赋值“;而
copy
中的实现是”赋值“,如何变成我们希望的创建新空间呢?可以使用inserter
,将目的端的迭代器改成是插入的动作,insert_iterator
中进行了operator=
函数的重载,所以使得copy
中调用的*result = *first
操作变成了调用容器的insert
操作,而容器的insert
操作就会自己开辟一块空间存放数据;
X Adapter
之所以叫做X Adapter
,因为不知道要归到哪一类中。
ostream_iterator
说明:
std::ostream_iterator<int> out_it(std::cout, ",");
是将迭代器的第一参数绑定到cout
上,即放入的数据都显示到屏幕上;第二参数绑定为字符串,作为分隔符号使用;copy
中*
、=
、++
还有返回时会调用拷贝构造都作用在result
上,这四个动作要如何改变,才能使得满足我们的需求——元素打印到屏幕上?ostream_iterator
中通过操作符重载operator=
使其能够使用cout输出出来;ostream_iterator
是ostream
的适配器;
istream_iterator
说明:
std::istream_iterator<double> eos;
调用无参构造函数,std::istream_iterator<double> iit(std::cin);
调用有参构造函数,istream_iterator
将cin
记录到in_stream
中;++iit
调用istream_iterator
的operator++()
函数,读入数据;*iit
调用istream_iterator
的operator*
函数,返回读入的数据;
说明:
iit
在创建的时候就已经读入了值,所以*first
的时候可以取出值了;while
循环中一边读入数据,一边返回数据;
图中是写错一行代码报出的错误信息,所以读源码的意义就体现在这里,看到繁杂的错误信息,要有能力分析它们出现的原因。
这篇关于【侯捷】C++STL标准库与泛型编程(第三讲)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!