for_each各种情况下的函数对象使用方法

原创作者:http://oomusou.cnblogs.com

配合《C++ Template》(简体中文)使用 http://download.csdn.net/detail/qq2399431200/5471215 ,下载地址。(使用方法很重要,故进行转载一下)


for_each函数用法

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<intconst char*>("Element:"));

34}

 

执行结果

Element:1

Element:2

Element:3

 

Conclusion

STL的for_each()事实上很好用,不过由于限制很多,所以常令很多新手却步,本文试着将所有会遇到问题的地方都提出来讨论,包括 procedure based、object oriented、generics三种paradigm与for_each()的搭配都涵盖了,希望对各位有帮助。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值