C++訳分からなすぎワロタ (2) なぜ shared_ptr のcopy ctorはconst参照を取るのか
auto_ptr
の場合は,やや聞き慣れない "move ctor" しか無くて,それは非const参照を取るよね.shared_ptr
の場合でも,copy constructと言いつつカウンタをインクリメントするんだから非constになるんじゃないのか...? と不思議に思った.
template<class Y> #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) shared_ptr( shared_ptr<Y> const & r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() ) #else shared_ptr( shared_ptr<Y> const & r ) #endif : px( r.px ), pn( r.pn ) // never throws { }
で, px
が生ポインタで pn
がカウンタ (boost::detail::shared_count
) なのでそっちの copy ctor を見る:
shared_count(shared_count const & r): pi_(r.pi_) // nothrow #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { if( pi_ != 0 ) pi_->add_ref_copy(); }
(デバッグ用を除けば)単一のメンバ sp_counted_base * pi_
のみを持ち,それを共有して操作するラッパだった.よって最初の問いの答えとしては「参照カウンタは『constなポインタ』の先にぶら下がっており,ポインタが指す先が動く訳ではないので引数はconst参照であって良い」というものだった.const sp_counted_base * pi_
と定義されてた訳じゃないからね.まぁ,色んなところで出る話だ... 「constって何?」「deep vs shallow」とか,その辺.
折角なのでポインタの先で共有されてるカウンタ・オブジェクトの実体 sp_counted_base * pi_
において何が(破壊的に)行われてるのか見ていこう.しかしここからはアーキテクチャ依存なので*1,最近の私はWindows環境が専門のプログラマである*2都合上,とりあえずWin32における実装を見る:
void add_ref_copy()
{
BOOST_INTERLOCKED_INCREMENT( &use_count_ );
}
ネストがいい加減 深過ぎるので,これはWin32 API InterlockedIncrement
*3 に展開されるマクロ*4だと指摘するにとどめる.そしてそのAPIの中身はx86のアトミック命令 lock add
なのであった.(今回は ifdef
の,最も代表的と思われる場合だけに注目したが.)
いや,まぁそんなことは最初から想像が付いていたのだが... まとめの感想としては,C++の初期化・コピー・代入って難しいなー.(そしてBoostの実装はGNU Autotools並のややこしさだなー)
*1:http://www.boost.org/doc/libs/1_45_0/boost/smart_ptr/detail/sp_counted_base.hpp
*2:ここで「Windowsプログラマ」と言ったら「人生を書き換えたってのはあなたでしたか?」ということになってしまう.言葉遣いは正確に.
*3:http://msdn.microsoft.com/ja-jp/library/cc429236.aspx
*4:http://www.boost.org/doc/libs/1_45_0/boost/detail/interlocked.hpp