Item18避免使用vector<bool>
做為一個STL容器,vector<bool>有兩個問題.第一,它不是一個真正STL容器,第二,它并不保存bool類型.
除此以外,并沒有太多東西與本節題目有關(譯注,還不夠多嗎)
一個東西不能成為一個STL容器,只因為會有人說它是一個(譯注,:( ).一個東西要成為STL容器,必須滿足所有
列于C++標準23.1節的容器要求.在這些要求中,有這樣一條:如果C是一個T類型元素容器,并且C支持operator[]
那么以下代碼必須能夠編譯:
T *p = &c[0]; // initialize a T* with the address
// of whatever operator[] returns
換句話說,如果你使用operator[]來得到Container<T>中的一個T對象,你可以對它取地址從而得到一個指針.
(假設T沒有重載opeartor&.譯注:原句為This assumes that T hasn´t perversely overloaded operators.
從意譯)因此,如果vector<bool>可能成為容器,那么,這些代碼必須編譯通過:
vector<bool> v;
bool *pb = &v[0]; // initialize a bool* with the address of
// what vector<bool>::operator[] returns
但是它不能編譯.不能的原因是vector<bool>是一個偽容器(pseudo-container),它并不保存真正的bool,而是
打包bool以節省空間.在一個典型的實現中,每一個"bool"保存在"vector"中都是一個"bit",8-bit的一個字節
將保存8個bool.從內部來看,vector<bool>使用了與位域(bitfields)相同的思想來表示需要保存的bool值.
與bool值相似,位域也只有兩個值,但有它倆之間有一個重要的不同:可以創建指向真正的bool型的指針,但指
向單獨一位的指針卻非法.
考慮到指向單獨一位的指針非法,這為vector<bool>的設計擺出了難題.因為vector<T>::operator[]的返回值
是T&類型.如果vector<bool>保存真正的bool值,這不成問題.但是因為它沒有,vector<bool>::operator[]
(譯注:原文為()疑誤)不知如何返回一位的引用,并不存在這樣的東西.
為了解決它,vector<boo>::operator[]返回一個對象,其行為類似于位的引用,也稱為代理對象.(僅使用STL,
你并不需要明白什么是代理.它是一項值得了解的C++技術.關于代理的信息,參考More Effective C++的Item30
還有Gamma等人(就是GoF)的設計模式一書中Proxy章節).深入本質來看,vector<bool>可能類似于這樣:
template <typename Allocator>
vector<bool, Allocator> {
public:
class reference {...}; // class to generate proxies for
// references to individual bits
reference operator[](size_type n); // operator[] returns a proxy
…
}
現在,這些代碼不能編譯的原因就很明顯了.
vector<bool> v;
bool *pb = &v[0]; // error! the expression on tne right is
// of type vector<bool>::reference*,
// not bool*
因為它不能編譯,所以vector<boo>不滿足STL容器的需要.vector<bool>在標準中,它也滿足了大多數STL容器的需要
,但是它還不夠好.你寫的關STL容器的代碼越多,會越深刻地認識到這一點.當一天來到時,我保證,當你會寫
出一個模板,它只在可以取得容器元素的地址時才工作.到那時,你將突然明白容器和幾乎是一容器之間的區別.
也許你想知道為什么vector<bool>存在于標準中,而它并不是一個容器.答案是與一個貴族失敗的實驗有關.但讓我們
推遲一下討論,我有一個更緊迫的問題.如果vector<bool>應避免,因為它不是一個容器,那當需要一個vector<bool>時
應使用什么?
標準庫提供了兩個代替物,它們滿足幾乎所有需要.第一個是deque<bool>.deque提供幾乎所有vector提供的(唯一值得
注意的是reserve和capacity),并且deque<bool>是一個STL容器,它保存真正的bool值.當然,deque底層的內存不連續.
所以不能傳遞deque<bool>中的數據給一個期望得到bool數組的C API(參見Item 16),但是vector<bool>也不能作這一點
因為沒用可移植的方法取得vector<bool>中的數據.(Item16中的技術不能在vector<boo>上編譯.因為這種技術依賴于
能夠取得容器元素的指針.我提到過vector<bool>中不保存bool值吧?)
第二個vector<bool>的代替物是bitset.bitset不是一個STL容器,但它是C++標準庫的一部分.與STL容器不同,它的大小
(元素總數)在編譯期固定.因此,它不支持插入和刪除元素,近一步,因為它不是一個STL容器,它也不支持iterator.
與vector<bool>類似,它使用一個壓縮的表示法,使得每個值只占用一位.它提供vector<bool>的特殊成員函數,還包含
一系列操作位集(collection of bits)的特殊成員函數.如果不在乎沒有迭代器和動態大小,那么bitset也許正合你意.
現在我們來討論那個貴族的失敗的實驗,正是它將非STL容器的vector<bool>留在了標準庫中.我早先提到代碼對象在C++
程序設計中十分有用.C++標準委員會的成員當然也意識到了,他們決定開發vector<bool>做為一個演示.它說明STL如何
支持包含通過代理訪問元素的容器.一但這個例子出現在標準中,而且它說明得很詳細,開發者將有一個參考,來實現自己
的基于代理的容器.
可是,最終他們發現,不可能創建一種基于代理的容器,它滿足所有STL容器的需要.因為某種原因,他們失敗了,而開發中
的這個例子留在了標準中.也許有人將探尋vector<bool>存在的原因,但現實地說,這不影響什么.重要的是:vector<bool>
不滿足STL容器的需要;你最好不要使用它;deque<bool>和bitset是基本能滿足你的需要vector<bool>代替品.