smart_ptrはスマートなポインタか?(1)

id:okzkさんからSoftware Design 7月号のひなた先生で気になったところについて指摘をいただいた。


■ smart_ptrとは?


まず読者のために前説。ここで出てくるsmart_ptrというのは、yaneSDK3rdに実装されている、smart_ptrのことである。(→gggの24章)


■ 指摘1


つまりはそのsmart_ptrにポインタを渡す時に「アップキャストされていない本来の型」の
ポインタとして渡す必要があるぢゃないかと。
は正しいです。私のsmart_ptrを用いる場合、newはsmart_ptrのコンストラクタ以外のところでは書かないというコーディング方針を守る必要があります。(循環参照の問題があるのでboost::weak_ptrに相当するものがあるべきだとは思います。)


boostを使う場合でも、boost::shared_ptr , boost::weak_ptrを用いる場合、同じような制約をコーディング規約に課すことはよくありますよね?


■ 指摘2


個人的にはこんなC++のアヤシイところに立ち入らなくてすむよう
std::vector< boost::shared_ptr<T> >を使う気がします。

手元にsmart_ptrが無いのなら、std::vector< boost::shared_ptr<T> >(以下、ptr_vectorと呼ぶ)を使うというのは現実的だと思います。


しかしプログラマは、コンテナのデザイン自体を自分で決定できるとは限りません。特に、C++でnew Hoge[n]と書いた場合、n個のオブジェクトは自動的にリニアなメモリに配置されますから、おそらく一番配列に関して多用されるコンテナデザイン(メモリ上のイメージと呼ぶべき?)は、この形です。


それゆえ、DLL側にHoge*が引数として渡ってきて、自分がそのDLLを書かなければならない、などということは度々起こりえます。これはptr_vectorでは解決にならないのです。


「ptr_vectorHoge配列のそれぞれの要素、すなわちHoge[0],Hoge[1],…,Hoge[n-1],Hoge[n]を指し示すようにすればいいじゃないか」(※1)と言われるかも知れませんが、これは.NETでmanagedとunmanagedのコードの間で行なわれるマーシャリングにも似ていて、このオーバーヘッドが馬鹿にならない場合があります。


また例えば Tの派生型のS型の配列があった場合、Sの配列のそれぞれの要素をptr_vectorから上記の※1のように指し示すようなコードが必要になります。ところが、sizeof(T)!=sizeof(S)のとき、&S[i] == (T*)&S[0] + i が成り立たないため、結局sizeof(S)がわからない限り、上記の※1を行なうことは出来ません。(※2)


つまり、このようにオブジェクトの配列が渡されてくるケースでsizeof(S)がわからないなら、smart_ptrはもちろん、ptr_vectorも無力なのです。よって『smart_ptrにポインタを渡す時に「アップキャストされていない本来の型」のポインタとして渡す必要がある(ので駄目だ)』という指摘と『std::vector< boost::shared_ptr<T> >を使う(ならば解決するだろう)』というスタンスとにはそこはかとなく矛盾を感じます。


■ 結論


smart_ptrは特定の条件下においては「ptr_vectorを用いたなら※1にかかるはずのコスト」を省略できるわけですから、smart_ptrの万能性を否定してもsmart_ptrの有効性の否定にはつながらないです。要するに、smart_ptrは、使える状況があると思うなら使ってくださいってなところで。


■ 関連トピック


動的に型サイズを求めるスマポ → id:shinichiro_h:20060623#1150996387