一直在使用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,這個讓人有點郁悶.要用的時候最好用宏定義替換一下,免得可移植性上出現問題.