一直在使用C/C++,对于循环语句while、do while、for,对于for情有独钟,因为其简洁、清晰、灵活。访问数组类型的变量,只有for写出来的语句是最易于阅读的,如:
int arr[N] = {/**/};
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,不再给人一种清晰的感觉了。或许因为这个程序比较短,还没有太大的感觉,当回头去看自已的程序中,有不少这样的写法时,我就觉得一阵心烦。改改?
for(size_t i = 0; i < m_vecData.size(); ++i)
{
if(!m_vecData[i].IsBusy())
m_vecData[i].DoSomeThing(param);
}
不错,还是简单点好啊。但是因为这里举的是vector的例子。如果是list或是别的什么容器,就行不通了。
其它的高级语言,都提供了foreach或是for in语句,写出来就很清晰:
foreach(item in m_vecData)
{
if(!item.IsBusy())
item.DoSomeThing(param);
}
C++是不是也可以这么简单?好象STL中也有一个for_each,试着改写一下:
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,要来得爽快一些。或许还有别的办法:
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));
天哪,这种写法好象更恐怖。而且,还不是每种情况都能用的:
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:
BOOST_FOREACH(cosnt MyClass& item, m_vecData)
{
if(!item.IsBusy())
item.DoSomeThing(param);
}
Oh Yeah!
好了,问题来了,为什么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);
}
不过,考虑到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>>提案写的很清楚了。
同时,假如lambda提案要是能再被通过的话,那就真的要开心了:
for_each(
m_vecData,
<>(item) extern(param)
{
if(!item.IsBusy())
item.DoSomeThing(param);
}
);
Cool!
不过,VC++2008倒是增加了foreach功能,不过关键字不是foreach,而是for each,这个让人有点郁闷.要用的时候最好用宏定义替换一下,免得可移植性上出现问题.