for_each的各种情况下的使用详解
原创作者: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()的搭配都涵盖了,希望对各位有帮助。
for_each的各种情况下的使用详解的更多相关文章
- linux下tar命令详解
linux下tar命令详解 tar是Linux环境下最常用的备份工具之一.tar(tap archive)原意为操作磁带文件,但基于Linux的文件操作机制,同样也可适用于普通的磁盘文件.ta ...
- 在telnet下操作memcache详解(操作命令详解)
这篇文章主要介绍了在telnet下操作memcache详解,telnet下的memcache操作命令详解,需要的朋友可以参考下 在定位问题.测试等时候经常需要对memcache的数据进行一些操作,但是 ...
- [r]Ubuntu Linux系统下apt-get命令详解
Ubuntu Linux系统下apt-get命令详解(via|via) 常用的APT命令参数: apt-cache search package 搜索包 apt-cache show package ...
- Linux下chkconfig命令详解(转)
Linux下chkconfig命令详解 chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. ...
- Linux知识积累(4) Linux下chkconfig命令详解
Linux下chkconfig命令详解 chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. ...
- Linux下top命令详解
Linux下top命令详解 top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.top是一个动态显示过程,即可以通过用户按键来不断刷 ...
- 转载的 Linux下chkconfig命令详解
Linux下chkconfig命令详解 chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. ...
- Linux下桥接模式详解一
注册博客园已经好长时间,一直以来也没有在上面写过文章,都是随意的记录在了未知笔记上,今天开始本着分享和学习的精神想把之前总结的笔记逐步分享到博客园,和大家一起学习,一起进步吧! 2016-09-20 ...
- linux下IPTABLES配置详解 (防火墙命令)
linux下IPTABLES配置详解 -A RH-Firewall-1-INPUT -p tcp -m state --state NEW -m tcp --dport 24000 -j ACCEPT ...
随机推荐
- HDU 1862 EXCEL次序 (排序水问题)
Problem Description Excel对能够记录一组由任意列排序指定.现在,请把你编译的代码类似特征. Input 測试输入包括若干測试用例. 每一个測试用例的第1行包括两个整数 N ...
- 解决 Xcode7 中多个模拟器的办法
转自: http://www.oschina.net/code/snippet_196012_50574 1.关闭xcode 2.终端输入 sudo killall -9 com.apple.Core ...
- MySQL无法使用、导入中文数据乱码
1,新版的MySQL无法使用 装的新版的mysql-installer-community-5.6.14.0.msi,无法使用(无法导入地图数据,卸载重装mysql_5.6.13.msi,无法启动). ...
- Python3.5.1 下使用HTMLParser报错
pip 安装HTMLParser之后,import HTMLParser 使用的时候,报错"ImportError:Can't not find module markupbase" ...
- Codeforces 484A - Bits 二进制找1
这题可以根据l, r 在二进制下的长度进行分类. l 的长度小于 r 的时候,有两种可能,一种是r 在二进制下是 1* 这种样子,故答案取 r : 一种是取答案为 (1LL << (r ...
- 基于Sql Server 2008的分布式数据库的实践(五)
原文 基于Sql Server 2008的分布式数据库的实践(五) 程序设计 ------------------------------------------------------------- ...
- android Bitmap围绕一个点进行旋转
在项目中需要使用定位功能,也就是一个点围绕一个圆心进行旋转,查看了canvas的函数也就只有一个 canvas.drawBitmap(bitmap, matrix, paint)通过使用Matrix来 ...
- phantomjs环境搭建已经运行
1.下载phantomjs http://phantomjs.org/ 2.运行 新建phantomjs.bat,记得改目录路径 里面内容为: D:\java\phantomjs\phantomjs. ...
- c# 课堂总结7--函数
函数:数据类型-变量类型-运算符号表达式-语句(顺序,分支,循环)-数字 程序里的函数:能完成一个相对独立功能的代码块.数学里的函数:高度抽象.函数四要素:函数名,输入(参数),输出(返回值类型),加 ...
- ASP.NET给Table动态添加删除行,并且得到控件的值
ASP.NET给Table动态添加控件并且得到控件的值 由于跟老师做一个小的项目,可是我自己又不太懂js,所以一直为动态建立表格并且能动态的取值和赋值感到苦恼.起初在网上找到了一些js资源,解决了动态 ...