沐枫小筑(C++)

爱老婆,要比编程多一点...

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  18 Posts :: 0 Stories :: 84 Comments :: 0 Trackbacks

公告

沐枫的个人主页

eoool.com生成

eoool.com生成

常用链接

留言簿(8)

我参与的团队

搜索

  •  

积分与排名

  • 积分 - 56009
  • 排名 - 397

最新评论

阅读排行榜

评论排行榜

    一直在使用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 on 2007-09-26 19:51 沐枫 阅读(16857) 评论(11)  编辑 收藏 引用 所属分类: C++

Feedback

# re: 从for到foreach 2007-09-26 22:05 danielwyo
for(vector<MyClass>::const_iterator iter = m_vecData.begin(); iter != m_vecData.end(); ++iter)
{
if(!iter->IsBusy())
iter->DoSomeThing(param);
}
----------------------------------
这个用的最多的了, 也很直观吧, 干嘛一定要强调使用foreach呢? 至于对C++新版本的期待, 我已经不抱希望了, 所谓的C++0x也不知道N年才出来, 而正式可以广泛使用的编译器, 那更是不知道要到什么时间了.   回复  更多评论
  

# re: 从for到foreach 2007-09-27 06:29 danielwyo
突然注意到了, 你的名字是ly, 跟我老婆的简写一样. 后面一个liny, 一看更象.   回复  更多评论
  

# re: 从for到foreach 2007-09-27 08:33 蚂蚁终结者
用for来处理container确实比较麻烦,有时候for语句还得折行。
估计新的特性可能还要等不少时间...  回复  更多评论
  

# re: 从for到foreach 2007-09-27 15:13 沐枫
并非完全要期待C++0x,那玩意儿至少要2年呢。

主要是为了希望能提高c++代码的可读性。
10多年前,刚学习C++的时候,为其倾倒,首要的,也是它在语言级上支持类,使得数据与算法的封装变得直观。
后来又加上函数重载和操作符重载,使得表达式变得简单清晰。
再后来,模板的出现,使得消除重复代码的同时还可以得到类型安全的保证。
这一切都是喜欢C++的理由。

然后,最终发现,这一切也变成容易写出坏味道甚至难以维护的代码的祸首。C++语法太过于低级,没有更高一级的抽象,造成了对很多人最终难学难用难看的印象和后果。

既然无法从语法级别上进行改进,那就只好从程序库中补充。因此,象BOOST等一系列的库,都在这上面花了不少力气。有许多人说这些库用的“技巧"旁门左道,因此而抵制。事实上,它们只是为了能够让别的人不需要用很深刻的技巧,而打造了一批让我们可以很顺手使用的程序库。

foreach 如此,format和assign如此,lambda,functional,bind,xpressive 等等,无不如此。

正是这些库能简化并提高程序设计的效率和可读性,才有了C++0x的提案和实践,也才有了C++语言的进化。  回复  更多评论
  

# re: 从for到foreach 2007-09-27 16:12 沐枫
@danielwyo
姓林的重名的实在是没有办法避免,我周围常能找到重名的。更何况拼音。
---
上面的例子实在是简单,因此,还不觉得那个iterator循环难看到哪去。一旦复杂度提高了,再那么写就很不顺眼了。
既使用foreach,也顶多改善一些罢了。

倒是对于.net引入的LINQ语法,很是向往。SQL当初面世的时候,曾被推宠,但直到今天也仅用于数据库。真正开始在一般的程序设计上普及,看来看去也只有LINQ了。

象上面的例子,用C#3.0写就是:
m_vecData
  .
Where(item => !item.IsBusy())
  .
All(item => item.DoSomeThing(param));
LINQ则:
var s = from item in m_vecData
        
where !item.IsBusy()
        
select item;
s.
All(item => item.DoSomeThing(param));

如果是用Ruby:
m_vecData.each {|item| !item.IsBusy() and item.DoSomeThing(param)}
  回复  更多评论
  

# re: 从for到foreach 2007-09-27 18:35 danielwyo
具体的情况确实不好说, 而针对复杂的for循环, foreach也会变得复杂. 我觉得其实这个并没有占有多少优势.

而sql语法本身有一定的缺陷, 被推崇为最新代语法, 其实在软件开发上并不占有优势, 个人认为, 它仅仅是更加接近英语语法而已, 并不是什么重要的改进. 很多人接触sql语法也并没有觉得比其它语法好学.

其实我就是你上面提到的抵制那些库的人, 不过boost还好, 至少其中的regex等比较实用的东西我还是用的比较多. 我还是更加喜欢ace这种更加实用的产品.   回复  更多评论
  

# re: 从for到foreach 2007-09-27 22:15 missdeer
感觉BOOST_FOREACH好像可能会有性能问题、、
不过std::for_each+boost::bind/boost::lambda简直就是无敌~  回复  更多评论
  

# re: 从for到foreach 2007-09-27 22:53 沐枫
@missdeer
好象、可能,这个说的很不地道。
性能的确会稍差,但差的是常数级别。循环体内是不差的。也就是说,大多数情况下可以忽略。  回复  更多评论
  

# re: 从for到foreach 2007-10-23 16:03 阿铁
沐枫,你好!
不提供foreach,就没有限制了,那么你想diy个forloop也行了.  回复  更多评论
  

# re: 从for到foreach[未登录] 2010-05-17 10:14 123
for_each 性能比 纯for加上迭代要高一些  回复  更多评论
  

# re: 从for到foreach 2011-01-26 16:43 mickey
C++从VS2005之后就有for each的啊。
vector<MyClass> myVector;
for each(auto element in myVector)
{
//xxx
}  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理