読者です 読者をやめる 読者になる 読者になる

配列クラス、capacityが限界のときにpush_backしたらどうするのが正しい?

本当にあった話。
「配列にpush_backしたい」というのは意外と望まれる動作のようだ。
std::vectorでいいじゃん、と思うかもしれないが、vectorじゃ嫌だ、vectotは使えない!!という人がいる。
何故かといえば、vectorはpush_backしてcapacity不足だとアロケータからメモリを引っ張ってくる。(デフォルトは概ねnew)
ここで出る不満。
「知らないうちにメモリ使用量が増える!」
「メモリが断片化する!」


じゃ、どうなればいいのかしら。
では、
push_backできる固定長配列の代替として考え得る最善の実装である
(ただし、vectorやboost::arrayのように配列とのメモリ互換性はない)
boost::circular_buffer
のばやい。
boost::circular_bufferは循環バッファであり、固定長配列のように使える。
capacityが一杯のときにpush_backするとところてん式に頭が落ちる。
すると、
「頭が落ちるなんて困る!!」


じゃ、どうしたらいいかね?
愚直な提案としての一案。
追加できなければ、
push_backでfalseを返しコンテナを変化させない。


うー、
これは最低に愚直。
falseが返ってきたところでどうしようもない。
std::vectorやboost::circular_bufferの方がよっぽど素直で確かな実装だ。
確かにstd::vectorもメモリが足りなくて増やせないこともある。
boost::circular_bufferのように頭が抜け落ちては困ることもある。


でも、その場合は追加する前にcapacityを超えないかどうかをassertでもなんでもチェックすればいいのだ。
追加してみて出来なかったらコンテナは変化せずfalseを返す、なんていう挙動を「コンテナが」示す必要はない。
実装は使い手が望むようにできているわけではなく、アルゴリズムに従い適切に動作するように作られている。


なになに、falseが返ってきたら追加できるようにコンテナを操作すれば良いって?
いや、追加できるか出来ないかは事前にわかるのだから、
追加してみて、というのはおかしな話だ。
操作の結果が分からないなら、それをリクエストすることは正しい。
だが、操作の結果が分かるのに、それをリクエストしてfalseを期待するのは正しいとは言えない。


要するに、最も最善なのは適切なサイズを最初に確保しておく、ないしはreserveしておくこと。
そうすれば、
std::vectorでも
boost::circular_bufferでも困らない。
push_backでfalseを返すような自前の実装を書く必要はない。
追加できなくて糞バグになるくらいならvectorが余程安全だ。
断片化といっても、配列の示すサイズはそうたいしたことはないだろうし、
たいしたことがあるのなら、余裕を持ってreserveすべきだからだ。