zoukankan      html  css  js  c++  java
  • 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()的搭配都涵盖了,希望对各位有帮助。

  • 相关阅读:
    Keras猫狗大战四:数据增强+添加dropout层,精度达83%
    Keras猫狗大战三:加载模型,预测目录中图片,画混淆矩阵
    左边列表多选可以批量移动到右边列表,右边列表页可以批量移动到左边列表,可以实现前端自动搜索 ajax 传递List
    两个list框联动-转载
    mybatis
    lambda
    sprint 单元测试
    java项目部署测试服务器二级域名解决方案
    java cookie 单点登录和登录拦截器 从实现到原理
    redis 概述和阿里云redis搭建和java后台获取
  • 原文地址:https://www.cnblogs.com/riskyer/p/3315592.html
Copyright © 2011-2022 走看看