VS2008 到 VS2010 STL關聯容器set 的一點變化

VS2008 VS2010 STL關聯容器set 的一點變化

最近在把項目從vs2008移植vs2010的時候, 發現在vs2008下編譯通過的代碼卻在vs2010編譯時出現錯誤。下面是代碼的一個簡化的示例:

vector<int> v(10, 1);

set<int> s(v.begin(), v.end());

   

set<int>::iterator i = s.begin();

(*i) = 0; // 這行賦值導致VS2010編譯失敗

 

這段代碼在VS2008中順利通過編譯,但在VS2010中出現以下錯誤:

error C3892: 'i' : you cannot assign to a variable that is const

 

這說明編譯器認爲i是一個const變量, 可我們明明定義iset<int>::iterator而不是set<int>::const_iterator, 是哪裏出了問題?難道VS2010做了一些特殊處理?可這種特殊處理爲什麼要導致不能向後兼容呢?要想弄清楚怎麼回事, 最直接的方法就是看看VS2010set的源代碼,其中有這麼一段:

 

typedef _Tree_const_iterator<_Mybase> const_iterator;

 

typedef typename _STD tr1::conditional<

_STD tr1::is_same<key_type, value_type>::value, const_iterator,

_Tree_iterator<_Mybase> >::type iterator;

 

上面的代碼說明const_iterator依然具有const屬性,但對於iterator的定義要依賴於key_typevalue_type是否是同一種類型,而關聯容器set就正是keyvalue是同一種類型的容器,所以上面的定義相當於:

typedef const_iterator iterator;

所以對於set來說iteratorconst_iterator都具有const屬性,不能修改set容器中的值。這就解釋了爲什麼一開始的復值語句爲什麼會失敗。

 

接下來的問題是爲什麼VS2010要做這樣不向後兼容的改變?爲了更加安全?我查閱了C++ 98 C++ 2003的標準, 其中關於關聯容器iterator的描述並沒有明確指出setiterator必須是const的。之後在C++標準定義網站上在標準庫的defect report中找到了答案, defect 103 描述了爲什麼要將setiterator改爲const的。下面是摘錄的一部分:

103. set::iterator is required to be modifiable, but this allows modification of keys

Section: 23.2.4 [associative.reqmts] Status: CD1 Submitter: AFNOR Opened: 1998-10-07 Last modified: 2008-09-26

Proposed resolution:

At the end of paragraph 5: "Keys in an associative container are immutable."

At the end of paragraph 6: "For associative containers where the value type is the same as the key type, both iterator and const_iterator are constant iterators. It is unspecified whether or not iterator and const_iterator are the same type."

這樣就避免了由於程序員的疏忽而更改了setkey,從而導致了set中的元素不再有序。由此看來VS2010是符合最新的C++標準的。其實VS2008也是符合C++標準的,因爲VS2008當時標準並沒有明確說明setkey能否修改,所以VS2008兩種都支持,它定義了一個宏_HAS_IMMUTABLE_SETS,缺省情況下這個宏被定義爲0,一次setkey是可以被修改的,如果想得到一個key不能被修改的set,只需要定義宏_HAS_IMMUTABLE_SETS1即可。這也說明了VS2008的態度,認可這種key可修改的set在多數情況下被使用,因爲一般每人會去自定義隨編譯器分發的STL庫。

 

最後,我想說的是在Effective STL的第22條中,scott指出切勿直接修改setmultiset中的鍵。之所以這樣是因爲會導致不可移植的代碼,因爲有的編譯器允許修改key,而有的就不允許(比如gcc),並且其中還提到了怎樣修改key纔是安全的做法。現在C++標準已經明確的指出set中的keyimmutable的,所以這個條款估計在下一版的時候會有些調整,不過裏面提到的一些方法,如用強制轉換來達到修改key的目的和其他的建議仍然有效,有興趣的朋友可以看看。

 

參考文章:

1.    Effective STL 22

2.    http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#103

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章