for_each的各种情况下的使用详解

时间:2023-03-08 16:00:58

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

配合《C++ Template》(简体中文)使用 http://download.****.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<int, const char*>("Element:"));

34}

执行结果

Element:1

Element:2

Element:3

Conclusion

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