一直在使用C/C++,对于循环语句while、do while、for,对于for情有独钟,因为其简洁、清晰、灵活。访问数组类型的变量,只有for写出来的语句是最易于阅读的,如:
然而,这种情况,到了STL时,就有些变味了:
其它的高级语言,都提供了foreach或是for in语句,写出来就很清晰:
1. notBusyClass不能是vector<const MyClass&>,因为不能建立指向引用的指针。这就要求MyClass是可拷贝的。但就算是可拷贝的,有时候拷贝成本也是很高的。
2. MyClass::DoSomeThing的参数不能是引用(我们常定义参数为:const Param&),因为不能定义引用的引用这种类型。
3. 一旦出现错误,这错误信息会让人极其昏倒。
看来单靠标准C++是不成的。Boost的lambda的库似乎很不错,用用:
好了,问题来了,为什么C++不直接在语言中提供foreach这个功能呢?
个人认为,原因有几点:
1. C/C++除了数组外,没有内置的容器,因此for语句足矣。
2. 当C++进化到STL的时候,C++标准委员会根本没空去考虑其它的。
而其它高级语言之所以内置了foreach,就是因为它们一开始就提供了标准的容器库和迭代/枚举接口,因此提供foreach就顺理成章了。
现在,总算C++开始考虑,由模板引入而造成的代码复杂性的问题,这的确是Cpper的福音。因此,一系列相关的提案被提交。牵涉到上面代码中的提案就有: Decltype, Lambda expressions and closures for C++, proposal for new for-loop。
其中,最符合foreach要求的就是新的for循环。采用这个语句,上面的程序就可以这么写:
不过,考虑到Decltype&auto提案已经被采纳,新的for-loop就不知道能不能再被采纳。因为使用Decltype&auto后,程序可以这么写:
同时,假如lambda提案要是能再被通过的话,那就真的要开心了:
不过,VC++2008倒是增加了foreach功能,不过关键字不是foreach,而是for each,这个让人有点郁闷.要用的时候最好用宏定义替换一下,免得可移植性上出现问题.
int
arr[N]
=
{
/*
*/
};
for ( int i = 0 ; i < N; ++ i)
printf( " arr[%d] = %d/n " , i, arr[i]);
for ( int i = 0 ; i < N; ++ i)
printf( " arr[%d] = %d/n " , i, arr[i]);
然而,这种情况,到了STL时,就有些变味了:
for
(vector
<
MyClass
>
::const_iterator iter
=
m_vecData.begin(); iter
!=
m_vecData.end();
++
iter)
{
if ( ! iter -> IsBusy())
iter -> DoSomeThing(param);
}
这么长的一个for,不再给人一种清晰的感觉了。或许因为这个程序比较短,还没有太大的感觉,当回头去看自已的程序中,有不少这样的写法时,我就觉得一阵心烦。改改?
{
if ( ! iter -> IsBusy())
iter -> DoSomeThing(param);
}
for
(size_t i
=
0
; i
<
m_vecData.size();
++
i)
{
if ( ! m_vecData[i].IsBusy())
m_vecData[i].DoSomeThing(param);
}
不错,还是简单点好啊。但是因为这里举的是vector的例子。如果是list或是别的什么容器,就行不通了。
{
if ( ! m_vecData[i].IsBusy())
m_vecData[i].DoSomeThing(param);
}
其它的高级语言,都提供了foreach或是for in语句,写出来就很清晰:
foreach
(item
in
m_vecData)
{
if ( ! item.IsBusy())
item.DoSomeThing(param);
}
C++是不是也可以这么简单?好象STL中也有一个for_each,试着改写一下:
{
if ( ! item.IsBusy())
item.DoSomeThing(param);
}
struct
IfNotBusyThenDoSomeThing
{
IfNotBusyThenDoSomeThing ( const Param & param)
: param_(param)
{}
void operator () ( const MyClass & item)
{
if ( ! item.IsBusy())
item.DoSomeThing(param_);
}
private :
const Param & param_;
};
for_each(m_vecData.begin(), m_vecData.end(), IfNotBusyThenDoSomeThing (param));
不错,for语句简单了,但是却多了
IfNotBusyThenDoSomeThing的定义,这代码可是多了好几倍。要是每个循环都要来这么一下,我还不如直接写for,要来得爽快一些。或许还有别的办法:{
IfNotBusyThenDoSomeThing ( const Param & param)
: param_(param)
{}
void operator () ( const MyClass & item)
{
if ( ! item.IsBusy())
item.DoSomeThing(param_);
}
private :
const Param & param_;
};
for_each(m_vecData.begin(), m_vecData.end(), IfNotBusyThenDoSomeThing (param));
vector
<
MyClass
>
notBusyClass;
remove_copy_if(m_vecData.begin(), m_vecData.end(), inserter(notBusyClass, notBusyClass.begin()), mem_fun_ref( & MyClass::IsBusy));
for_each(notBusyClass.begin(), notBusyClass.end(), bind2nd(mem_fun_ref( & MyClass::DoSomeThing), param));
天哪,这种写法好象更恐怖。而且,还不是每种情况都能用的:
remove_copy_if(m_vecData.begin(), m_vecData.end(), inserter(notBusyClass, notBusyClass.begin()), mem_fun_ref( & MyClass::IsBusy));
for_each(notBusyClass.begin(), notBusyClass.end(), bind2nd(mem_fun_ref( & MyClass::DoSomeThing), param));
1. notBusyClass不能是vector<const MyClass&>,因为不能建立指向引用的指针。这就要求MyClass是可拷贝的。但就算是可拷贝的,有时候拷贝成本也是很高的。
2. MyClass::DoSomeThing的参数不能是引用(我们常定义参数为:const Param&),因为不能定义引用的引用这种类型。
3. 一旦出现错误,这错误信息会让人极其昏倒。
看来单靠标准C++是不成的。Boost的lambda的库似乎很不错,用用:
for_each(m_vecData.begin(), m_vecData.end(),
if_then( !bind( & MyClass::IsBusy, _1),
bind( & MyClass::DoSomeThing, _1, param)));
不错,好了一些,但是还是很不好看。有没有更好的?有,boost1.34新加入的BOOST_FOREACH:
if_then( !bind( & MyClass::IsBusy, _1),
bind( & MyClass::DoSomeThing, _1, param)));
BOOST_FOREACH(cosnt MyClass
&
item, m_vecData)
{
if ( ! item.IsBusy())
item.DoSomeThing(param);
}
Oh Yeah!
{
if ( ! item.IsBusy())
item.DoSomeThing(param);
}
好了,问题来了,为什么C++不直接在语言中提供foreach这个功能呢?
个人认为,原因有几点:
1. C/C++除了数组外,没有内置的容器,因此for语句足矣。
2. 当C++进化到STL的时候,C++标准委员会根本没空去考虑其它的。
而其它高级语言之所以内置了foreach,就是因为它们一开始就提供了标准的容器库和迭代/枚举接口,因此提供foreach就顺理成章了。
现在,总算C++开始考虑,由模板引入而造成的代码复杂性的问题,这的确是Cpper的福音。因此,一系列相关的提案被提交。牵涉到上面代码中的提案就有: Decltype, Lambda expressions and closures for C++, proposal for new for-loop。
其中,最符合foreach要求的就是新的for循环。采用这个语句,上面的程序就可以这么写:
for
(
const
MyClass
&
item : m_vecData)
{
if ( ! item.IsBusy())
item.DoSomeThing(param);
}
{
if ( ! item.IsBusy())
item.DoSomeThing(param);
}
不过,考虑到Decltype&auto提案已经被采纳,新的for-loop就不知道能不能再被采纳。因为使用Decltype&auto后,程序可以这么写:
for
(auto iter
=
m_vecData.begin(), end
=
m_vecData.end(); iter
!=
end;
++
iter)
{
if ( ! iter->IsBusy())
iter->DoSomeThing(param);
}
似乎还是复杂点是吧?但是有了decltype&auto后,foreach功能可以用程序库或宏的形式被模拟,BOOST_FOREACH就是 这么做的。具体模拟的方式<<proposal for new for-loop>>提案写的很清楚了。
{
if ( ! iter->IsBusy())
iter->DoSomeThing(param);
}
同时,假如lambda提案要是能再被通过的话,那就真的要开心了:
for_each(
m_vecData,
<> (item) extern (param)
{
if (!item.IsBusy())
item.DoSomeThing(param);
}
);
Cool!
m_vecData,
<> (item) extern (param)
{
if (!item.IsBusy())
item.DoSomeThing(param);
}
);
不过,VC++2008倒是增加了foreach功能,不过关键字不是foreach,而是for each,这个让人有点郁闷.要用的时候最好用宏定义替换一下,免得可移植性上出现问题.