从for到foreach

    一直在使用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的福音。因此,一系列相关的提案被提交。牵涉到上面代码中的提案就有: DecltypeLambda 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,这个让人有点郁闷.要用的时候最好用宏定义替换一下,免得可移植性上出现问题.

   
posted @ 2007-09-26 19:48 沐枫 阅读(...) 评论(...) 编辑 收藏