写像を表現するクラスの設計

写像 (mapping) の概念をクラスにする際に,既存の mapping を破壊するようなインターフェースを提供しなければ deep copy が本質的に必要なく, shallow copy だけでコピーのセマンティクスを安全に達成できるんじゃないのか,ということを思いつく.例えば

  • 0 -> 0
  • 1 -> 2
  • 2 -> 4
  • 3 -> 6

という mapping の情報を保持しているオブジェクト A があったとして, 0 -> 0 という既存の mapping を 0 -> 8 に書き換えるようなインターフェースが提供されていなければ, A の mutable なコピー B が shallow copy のみで作れて,たとえ(A と実体を共有している) B に何か新たな mapping の情報を書き込むとしても, deep copy を走らせなくて良い(COW を実行せずに実体に直接書き込んで良い)んじゃないかという発想.

B に新たに 4->8 という mapping を追加したとしても,A が保持していた mapping の情報は依然破壊されない.ただし,この考え方が有効になるのは,各状態における定義域に対してのみ写像の結果を保証している場合で,例えば A のオブジェクトの同一性は定義域 {0,1,2,3} に関して常に各々 {0,2,4,6} に写像されるということのみで定義されて,定義域外の値における写像の結果については関知しない(例えば 4 が何に写像されるかなどは知ったこっちゃない(A の同一性には関与しない)),というふうにしないといけない.
副作用のない純粋な関数として振舞う関数オブジェクトに COW のない handle-body idiom (pimpl) が適用できるのも,上記の論理を適用した結果だよにゃ〜,とか考える.
で,だからそれがどうしたっていう…….

Type Erasure in boost::shared_ptr

そういえば,Type Erasureの一番顕著な実例が boost::shared_ptr っていうの書くの忘れてた.

class C{};

boost::shared_ptr< void > p( new C );
// 参照カウントが 0 になるときちんと『C のポインタに対する』 delete を実行する

別に void だけに限らず,例えばデストラクタが仮想でない基底クラスの shared_ptr にその派生クラスのポインタを突っ込んだりしても安全に delete してくれたりする.
っていうか,上のコードがちゃんと C のデストラクタをちゃんと呼んで,正しくメモリを開放してくれるというのをなかなか信じてもらえなかった.