C++裡為何要定義類似size_type,difference_type,iterator這種型別?
更新:評論區有後續。
這是一個可以無限扯皮的問題。
size_type
VS
unsigned
沒什麼好說的,
unsigned
太小了。
我給你模擬一下,如果要是在 C++ 群討論
size_type
VS
size_t
會是什麼樣的情況。
Alice:
size_t s = v。size()
。
Bob:@Alice 太菜了,容器的
v。size()
返回
size_type
,不是
size_t
。
Alice:有區別?
size_type
不就定義成
size_t
嗎?
Bob:不,這是 implementation defined 。
Alice:不是,你知道
size_t
是啥嗎?
size_t
是
sizeof
的返回值,是一次能分配的最大記憶體。容器的 size 肯定小於等於
size_t
。
Bob:還是 naive 。如果容器是非連續的,例如
std::list
,容器的 size 當然可以超過
size_t
。
Alice:@Bob 我這是 v ,看命名是 vector 。就算是
std::list
,32 位的
size_t
是 32 位的,64 位的
size_t
是 64 位的,能給你用的最大記憶體就是 2^64,怎麼可能超越?
Cara:有人玩吃雞嗎?
Bob:16 位的呢?
Alice:16 位怎麼了?
Bob:16 位的
size_t
是 16 位的,但是別忘了 DOS 是分段的。20 位地址線,
std::list
的 size 當然可以大於
size_t
。至少不能低於 20 位。只要是分段的體系結構都存在這個問題。
Alice:現在還有分段?
Ela:一個程序可以跨越 segment 使用所有 20 位記憶體嗎?
Alice:不是,你都真實模式了,說個 JB 呢?
Bob:我就舉個例子,現在 IBM 好像也有分段的。
Cara:有人玩吃雞嗎?
看下這本書吧,都是抽象,抽象再抽象
首先,size_t和平臺有關,比如x86、x64。需要儘量符合平臺記憶體大小,但又不會太大以至浪費。
2。 其次,容器定義不同size_type是有實際用途的。
比如boost::container::vector,可以用模板引數選擇用size_t還是uint32_t,甚至uint8_t也是可以的。這樣可以縮小容器本身的記憶體駐留(memory footprint)。
如果寫程式碼時,已經把size_type考慮在內,那麼就可以用boost::container::vector無縫替換std::vector。
3。 difference_type有符號,常用來表示記憶體地址之間的距離(std::distance)。因為size_type一般無符號,所以difference_type可能比size_type大。比如記憶體地址上限是4G,理論上用int32_t是無法表示所有distance的,需要int64_t。實踐上可能用int32_t混過去了,32位的windows程式通常記憶體不能超過2g,我覺得和這個是有關係的。
4。 iterator是c++的標誌性設計,解耦了容器與演算法。iterator實現五花八門,最簡單的是記憶體指標,複雜的可以很複雜,比如boost::adjacency_list的edge iterator。功能是int完全不能比擬的。
5。 對於size_t,signed vs unsigned是個非常深奧的問題。實踐上size_t用signed非常好,不存在signed vs unsigned比較,統一了size_type與difference_type,減少了很多index減法bug。但是size這個東西,概念/本質上是非負的,用signed影響閱讀理解。而程式是給人讀的,降低心智負擔/
無歧義
很重要。c++選擇了unsigned,態度就是
概念
比實踐更重要。c++一直受此詛咒,concept陰魂不散。
總之c++是個隨心所欲的語言,為了滿足這種隨心所欲,代價就是基礎設施的異常複雜。
我的角度可能有點奇怪。
C++ 標準庫工具需要定義這些巢狀型別,一定程度上是由於當初設計 STL 時( 1994 年開始)沒有
decltype
,從而泛型程式碼需要依賴顯式寫出來的巢狀型別名。
本來
我們不需要巢狀的
size_type
,只需要拿
size
函式的返回型別;
我們不需要巢狀的
iterator
,只需要拿
begin
函式的返回型別;
……
不過
difference_type
對於不能相減(一般是非隨機訪問)的迭代器還是有意義的。
至於
int
或
unsigned
為什麼有時不適合,可以參考別人的回答。
這個問題簡單,你說的那幾個型別都是 64 位的,int 跟 unsigned 一般是 32 位的,放 64 位資料進去會崩。
早年間習慣把pointer或者size放進int的史前程式設計師,它們留下的程式碼在後世基本都炸了,BOOM得不要不要的。
而且把64位放進32位的這種操作在特定情況下不會立刻崩,假如被覆蓋的部分恰好可寫的話,你會在另外一個莫名其妙的場合發現莫名其妙的bug。
別問我為什麼知道,這就是我幫史前程式設計師收拾殘局的親身經歷。