原创作者:http://oomusou.cnblogs.com
配合《C++ Template》(简体中文)使用 http://download.csdn.net/detail/qq2399431200/5471215 ,下载地址。
Introduction
学习过STL的container后,想要存取每一个iterator,你一定写过以下的程序
#include <vector>
#include <iostream>
using namespace std;
int main() {
int ia[] = {1, 2, 3};
vector<int> ivec(ia, ia + sizeof(ia) / sizeof(int));
for(vector<int>::const_iterator iter = ivec.begin(); iter != ivec.end(); ++iter) {
cout << *iter << endl;
}
}
执行结果
1
2
3
当时我觉得STL什么都好,就是以下这一串又臭又长
for(vector<int>::const_iterator iter = ivec.begin(); iter != ivec.end(); ++iter) {
若不常写,一時还会写不出來,其实若配合container,C++其实不应该这样像写循环,正确的方式该使用for_each(),语法会变得相当简单。
for_each()事实上是个function template,其实做如下[effective STL item 41]
template<typename InputIterator, typename Function>
Function for_each(InputIterator beg, InputIterator end, Function f) {
while(beg != end)
f(*beg++);
}
由以上source可知,for_each()只能配合global function和function object。
以下我们将对procedure based(基于程序)、object oriented(面向对象)、generics(通用)三种paradigm与for_each()搭配做探讨。
ProcedureBased与for_each()搭配
1.不传入参数
1 /*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3Filename : GenericAlgo_for_each_GlobalFunction.cpp
4Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
5Description : Demo how to use for_each with global function
6Release : 05/11/2007 1.0
7*/
8#include <iostream>
9#include <vector>
10#include <iostream>
11#include <algorithm>
12
13using namespace std;
14
15void printElem(int& elem) {
16 cout << elem << endl;
17}
18
19int main() {
20 int ia[] = {1, 2, 3};
21 vector<int> ivec(ia, ia + sizeof(ia) / sizeof(int));
22
23 for_each(ivec.begin(), ivec.end(), printElem);
24}
执行结果
1
2
3
只需将vector::begin(),vector::end()和global function name传给for_each()即可,再也不用for循环那种复杂的语法了。
2.传入参数
若要传参数给global function,就不能再只传global function name而已,必须透过ptr_fun()这个function adapter将global function转成function object,然后再用bind2nd()将参数bind成一个functionobject。
8#include <iostream>
9#include <vector>
10#include <iostream>
11#include <algorithm>
12#include <functional>
13
14using namespace std;
16void printElem(int elem, const char* prefix) {
17 cout << prefix << elem << endl;
18}
19int main() {
21 int ia[] = {1, 2, 3};
22 vector<int> ivec(ia, ia + sizeof(ia) / sizeof(int));
23
24 for_each(ivec.begin(), ivec.end(), bind2nd(ptr_fun(printElem), "Element:"));
25}
ptr_fun辅助构造一般函数指针的point_to_binary_function或是
pointer_to_unary_function适配器实例
构造一元函数指针适配申明如下:
template<typename Arg, typename Result>
pointer_to_unary_function<Arg, Result, Result (*)(Arg)> ptr_fun(Result (*_pfunc)(Arg));
首先,STL定义了binder2nd类,该类继承自unary_function,在类的函数运算体中完成对二元函数的参数传递和调用。binder2nd的实例构造通常比较冗长,bind2nd函数用于辅助构造binder2nd的一个实例(返回一个binder2nd类的对象,这个类应该重载了()运算符)。
bind2nd的原型声明为:
template<typename Operation, typename Type>
binder2nd<Operation> bind2nd(
const Operation& _Func,
const Type& _Right
);
binder2nd函数详解
http://stochasticquant.com/2012/04/c%E5%87%BD%E6%95%B0%E5%AF%B9%E8%B1%A1/
http://hi.baidu.com/ctrlaltz/item/f7fe4a8c0a5136d65f0ec151
执行结果
Element:1
Element:2
Element:3
ObjectOriented与for_each()搭配
1.不传入参数
使用function object
1 /*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3Filename : GenericAlgo_for_each_FunctionObject.cpp
4Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
5Description : Demo how to use for_each with function object
6Release : 05/11/2007 1.0
7*/
8#include <iostream>
9#include <vector>
10#include <iostream>
11#include <algorithm>
12
13using namespace std;
14
15struct printElem {
16 void operator() (int elem) {
17 cout << elem << endl;
18 }
19};
20
21int main() {
22 int ia[] = {1, 2, 3};
23 vector<int> ivec(ia, ia + sizeof(ia) / sizeof(int));
24
25 for_each(ivec.begin(), ivec.end(), printElem());//传入一个对象
26}
执行结果
1
2
3
2.传入参数
若使用function object,也可以将参数传给printElem(),通过constructor的技巧接收参数。
1 /*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3Filename : GenericAlgo_for_each_FunctionObjectWithParameter.cpp
4Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
5Description : Demo how to use for_each with function object with parameter
6Release : 05/11/2007 1.0
7*/
8#include <iostream>
9#include <vector>
10#include <iostream>
11#include <algorithm>
12
13using namespace std;
14
15struct printElem {
16 const char* _prefix;
17
18 printElem(const char* prefix) : _prefix(prefix) {}
19
20 void operator() (int elem) {
21 cout << _prefix << elem << endl;
22 }
23};
24
25int main() {
26 int ia[] = {1, 2, 3};
27 vector<int> ivec(ia, ia + sizeof(ia) / sizeof(int));
28
29 for_each(ivec.begin(), ivec.end(), printElem("Element:")); //调用构造函数构造了一个对象,重载了()运算符的类
30}
补充:
Function for_each(InputIterator beg, InputIterator end, Function f)
{ while(beg != end) f(*beg++); }
执行结果
Element:1
Element:2
Element:3
functionobject有很多种写法,但只要是function object都可以跟for_each()合作。
3.member_function与for_each()搭配
3.1 不传入参数
本文的重点来了,在物件导向世界里,最常用的就是for_each()配合member function,这该怎么写呢?直觉会这样子写
for_each(_doorVec.begin(), _doorVec.end(),&Door::open);
由于global function name本身就是一个pointer,所以想借由&Door::open传进一个address,但这样compile并不会过,正确解法是
for_each(_doorVec.begin(), _doorVec.end(), mem_fun_ref(&Door::open));
通过mem_fun_ref()这个function adapter将member function转成function object。
1 /*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : GenericAlgo_for_each_MemberFunctionObject.cpp
5Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6Description : Demo how to use for_each with member function with object
7Release : 05/11/2007 1.0
8*/
9#include <vector>
10#include <iostream>
11#include <algorithm>
12#include <functional>
13
14using namespace std;
15
16class Door {
17public:
18 void open() const {
19 cout << "open door horizontally" << endl;
20 }
21
22 void close() const {
23 cout << "close door horizontally" << endl;
24 }
25};
26
27class DoorController {
28protected:
29 vector<Door> _doorVec;
30
31public:
32 void addDoor(Door aDoor) {
33 _doorVec.push_back(aDoor);
34 }
35
36 void openDoor() const {
37 for_each(_doorVec.begin(), _doorVec.end(), mem_fun_ref(&Door::open));
38 }
39};
40
41int main() {
42 DoorController dc;
43 dc.addDoor(Door());
44 dc.addDoor(Door());
45 dc.openDoor();
46}
补充:
template<class_Result,class _Ty>
class mem_fun_ref_t : publicunary_function<_Ty,_Result>
{ // functoradapter (*left.*pfunc)(), non-const *pfunc
public:
explicit mem_fun_ref_t(_Result(_Ty::*_Pm)()):_Pmemfun(_Pm)
{ // construct from pointer
}
_Resultoperator()(_Ty&_Left)const //特别注意
{ // call function
return((_Left.*_Pmemfun)()); //_Left是vector中的参数
}
private:
_Result(_Ty::*_Pmemfun)(); // the memberfunction pointer
};
template<class_Result,class _Ty>
inline mem_fun_ref_t<_Result,_Ty>mem_fun_ref(_Result (_Ty::*_Pm)())
{ // return amem_fun_ref_t functor adapter
return (mem_fun_ref_t<_Result,_Ty>(_Pm));
}
执行结果
open door horizontally
open door horizontally
值得注意的是,mem_fun_ref()用在object的member function。若要搭配多型,vector必须放pointer(不明白什么意思),也就是得使用object pointer的member function,此时得使用mem_fun()将member function转成function object。
1 /*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : GenericAlgo_for_each_MemberFunctionObjectPointer.cpp
5Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6Description : Demo how to use for_each with member function with object pointer
7Release : 05/11/2007 1.0
8*/
9#include <vector>
10#include <iostream>
11#include <algorithm>
12#include <functional>
13
14using namespace std;
15
16class AbstractDoor {
17public:
18 virtual void open() const {
19 cout << "open door horizontally" << endl;
20 }
21
22 virtual void close() const {
23 cout << "close door horizontally" << endl;
24 }
25};
26
27class HorizontalDoor : public AbstractDoor {
28};
29
30class VerticalDoor : public AbstractDoor {
31public:
32 void open() const {
33 cout << "open door vertically" << endl;
34 }
35
36 void close() const {
37 cout << "close door vertically" << endl;
38 }
39};
40
41class DoorController {
42protected:
43 vector<AbstractDoor*> _doorVec;
44
45public:
46 void addDoor(AbstractDoor& aDoor) {
47 _doorVec.push_back(&aDoor);
48 }
49
50 void openDoor() const {
//mem_fun辅助构造mem_fun_t等成员函数,返回一元或二元函数对象
51 for_each(_doorVec.begin(), _doorVec.end(), mem_fun(&AbstractDoor::open));
52 }
53};
54
55int main() {
56 DoorController dc;
57 dc.addDoor(HorizontalDoor()); //具体多态过程参看3.1的补充处代码
58 dc.addDoor(VerticalDoor());
59 dc.openDoor();
60}
补充:
// TEMPLATE CLASSmem_fun_t
template<class_Result,
class _Ty>
class mem_fun_t
: publicunary_function<_Ty*,_Result>
{ // functor adapter (*p->*pfunc)(), non-const *pfunc
public:
explicit mem_fun_t(_Result(_Ty::*_Pm)())
: _Pmemfun(_Pm)
{ // construct from pointer
}
_Resultoperator()(_Ty*_Pleft)const
{ // call function
return((_Pleft->*_Pmemfun)());
}
private:
_Result(_Ty::*_Pmemfun)(); // the memberfunction pointer
};
执行结果
open door horizontally
open door vertically
使用了mem_fun()。
3.2传入参数
问题又来了,若要使member function也传入参数呢?这时得使用bind2nd将function object和参数bind在一起,变成另外一个新的function object。
1 /*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : GenericAlgo_for_each_MemberFunctionObjectPointerWithParameter.cpp
5Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6Description : Demo how to use for_each with member function with object pointer
7Release : 05/11/2007 1.0
8*/
9#include <iostream>
10#include <vector>
11#include <algorithm>
12#include <functional>
13
14using namespace std;
15
16class AbstractDoor {
17public:
18 virtual void open() const {
19 cout << "open door horizontally" << endl;
20 }
21
22 virtual void close() const {
23 cout << "close door horizontally" << endl;
24 }
25
26 virtual void openDoorBy(const char* name) const {
27 cout << name << " ";
28 open();
29 }
30};
31
32class HorizontalDoor : public AbstractDoor {
33};
34
35class VerticalDoor : public AbstractDoor {
36public:
37 void open() const {
38 cout << "open door vertically" << endl;
39 }
40
41 void close() const {
42 cout << "close door vertically" << endl;
43 }
44};
45
46class DoorController {
47protected:
48 vector<AbstractDoor*> _doorVec;
49
50public:
51 void addDoor(AbstractDoor& aDoor) {
52 _doorVec.push_back(&aDoor);
53 }
54
55 void openDoor() const { //mem_fun辅助构造函数返回一元或二元函数
56 for_each(_doorVec.begin(), _doorVec.end(), bind2nd(mem_fun(&AbstractDoor::openDoorBy), "John"));
57 }
58};
59
60int main() {
61 DoorController dc;
62 dc.addDoor(HorizontalDoor());
63 dc.addDoor(VerticalDoor());
64 dc.openDoor();
65}
执行结果
1 John open door horizontally
2John open door vertically
透过了bind2nd将参数结合后,成为一个新的function object。
Generics与for_each()搭配
1.FunctionTemplate
1.1不传入参数
在泛型世界里,那for_each()该怎么配合function template呢?
1 /*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3Filename : GenericAlgo_for_each_FunctionTemplate.cpp
4Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
5Description : Demo how to use for_each with function template
6Release : 05/11/2007 1.0
7*/
8#include <iostream>
9#include <vector>
10#include <iostream>
11#include <algorithm>
12
13using namespace std;
14
15template<typename T>
16void printElem(T elem) {
17 cout << elem << endl;
18}
19
20int main() {
21 int ia[] = {1, 2, 3};
22 vector<int> ivec(ia, ia + sizeof(ia) / sizeof(int));
23
24 for_each(ivec.begin(), ivec.end(), printElem<int>);
25 //for_each(ivec.begin(), ivec.end(), (void(*)(int))printElem);
26}
执行结果
1
2
3
若使用function template,有两种写法
一种是
for_each(ivec.begin(), ivec.end(), printElem<int>);
由于template function需要在compile时确定类型,所以要加上<int>确定为int类型。
另外一种写法
for_each(ivec.begin(), ivec.end(), (void(*)(int))printElem);
templatefunction并没有确定类型,但转成function pointer时,必须明确转成int类型的function pointer。
1.2 传入参数
若要如function object那样能传入参数呢?funtion template是可以,不过有些限制,若使用nontype parameter,只能使用以下三种类型
1.unsignedint/int、unsigned char/char或enum
2.pointer:pointer to object,pointer tofunction,pointer to member。
3.reference:reference to object,reference to function。
1 /*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3Filename : GenericAlgo_for_each_FunctionTemplateWithNontypeParameter.cpp
4Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
5Description : Demo how to use for_each with function template with nontype parameter
6Release : 05/11/2007 1.0
7*/
8#include <iostream>
9#include <vector>
10#include <iostream>
11#include <algorithm>
12
13using namespace std;
14
15template<typename T, int i>
16void printElem(T elem) {
17 cout << i << ":" << elem << endl;
18}
19
20int main() {
21 int ia[] = {1, 2, 3};
22 vector<int> ivec(ia, ia + sizeof(ia) / sizeof(int));
23
24 for_each(ivec.begin(), ivec.end(), printElem<int, 5>);
25}
执行结果
5:1
5:2
5:3
所以无法如function object那样可以传入字串或任意类型,最少在目前ISO C++标准是做不到的。
既然讨论了function template,那最具威力的class template是否也能搭配for_each()?
2.ClassTemplate
2.1 不传入参数
1 /*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3Filename : GenericAlgo_for_each_ClassTemplate.cpp
4Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
5Description : Demo how to use for_each with class template
6Release : 05/11/2007 1.0
7*/
8#include <iostream>
9#include <vector>
10#include <iostream>
11#include <algorithm>
12#include <functional>
13
14using namespace std;
15
16template<typename T>
17class printElem : public unary_function<T, void> {
18public:
19 void operator() (T elem) {
20 cout << elem << endl;
21 }
22};
23
24int main() {
25 int ia[] = {1, 2, 3};
26 vector<int> ivec(ia, ia + sizeof(ia) / sizeof(int));
27
28 for_each(ivec.begin(), ivec.end(), printElem<int>());
29}
执行结果
1
2
3
因为printElem只接受for_each()所传的参数,算是单参数而已,所以继承了unary_function<T,void>,因为for_each的定义
template <class InputIterator, class UnaryFunction>
UnaryFunction for_each(InputIterator first, InputIterator last, UnaryFunction f);
传进去的是UnaryFunction型別,第一个type parameter T表示传入的型別,第二个type parameter void,表示回传的型別,最后重新定义operator()。
2.2 传入参数
若要使class template也能传入参数,一样利用function object的技巧,借用constructor。
1 /*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3Filename : GenericAlgo_for_each_ClassTemplateWithParameter.cpp
4Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
5Description : Demo how to use for_each with class template & parameter
6Release : 05/11/2007 1.0
7*/
8#include <iostream>
9#include <vector>
10#include <iostream>
11#include <algorithm>
12#include <functional>
13
14using namespace std;
15
16template<typename T, typename U>
17class printElem : public unary_function<T, void> {
18private:
19 U _prefix;
20
21public:
22 printElem(U prefix) : _prefix(prefix) {}
23
24 void operator() (T elem) {
25 cout << _prefix << elem << endl;
26 }
27};
28
29int main() {
30 int ia[] = {1, 2, 3};
31 vector<int> ivec(ia, ia + sizeof(ia) / sizeof(int));
32
33 for_each(ivec.begin(), ivec.end(), printElem<int, const char*>("Element:"));
34}
执行结果
Element:1
Element:2
Element:3
Conclusion
STL的for_each()事实上很好用,不过由于限制很多,所以常令很多新手却步,本文试着将所有会遇到问题的地方都提出来讨论,包括 procedure based、object oriented、generics三种paradigm与for_each()的搭配都涵盖了,希望对各位有帮助。