VS2005のauto_ptrというかauto_ptr_refのバグ

以下のauto_ptrを使ったコード、VC6だとエラーになるんだけどVS2005だとコンパイルが通る。が、main()を抜ける時に走る~A()で落ちる。

#include<memory>
#include<iostream>

using namespace std ;

class A
{
public :
	A( void ){ cout << "ctor" << endl ; }
	virtual ~A( void ){	cout << "dtor" << endl ; }
} ;

void main( void )
{
	cout << "auto_ptr" << endl ;
	
	auto_ptr<A> ap_A = new A() ;	// (A) プログラムが落ちる。
//	auto_ptr<A> ap_A( new A() ) ;	// (B) こっちはVC6/VS2005で正常に動作する。
}

auto_ptr<A>にA *を引数にとるコピーコンストラクタが存在しないため、本来はエラーにならなければならない(たぶん)はずだが、下記にしめすauto_ptr_refが絡んできて暗黙の型変換が行われエラーにならない。具体的には、auto_ptr_refクラスのコンストラクタ(auto_ptr_ref<A>::auto_ptr_ref(void *))によって、(A)の箇所が次のようになる。

    • auto_ptr<A> ap_A = auto_ptr_ref( new A() )
template<class _Ty>
	struct auto_ptr_ref
		{	// proxy reference for auto_ptr copying
	auto_ptr_ref(void *_Right)
		: _Ref(_Right)
		{	// construct from generic pointer to auto_ptr ptr
		}

	void *_Ref;	// generic pointer to auto_ptr ptr
	};

この暗黙の型変換により、下記(D)にしめすauto_ptr_ref<A>を引数にとるコピーコンストラクタが呼び出される。
なお、正常に動作する上記(B)の場合は(C)のコンストラクタが呼び出される。

	explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()		// (C)
		: _Myptr(_Ptr)
		{	// construct from object pointer
		}

	auto_ptr(auto_ptr<_Ty>& _Right) _THROW0()
		: _Myptr(_Right.release())
		{	// construct by assuming pointer from _Right auto_ptr
		}

	auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()	// (D)
		{	// construct by assuming pointer from _Right auto_ptr_ref
		_Ty **_Pptr = (_Ty **)_Right._Ref;			// (D-1)
		_Ty *_Ptr = *_Pptr;					// (D-2)
		*_Pptr = 0;	// release old				// (D-3)
		_Myptr = _Ptr;	// reset this				// (D-4)
		}

この暗黙の型変換はたぶん意図されていないと思われるので、(D)のコピーコンストラクタ内の動作がおかしくなる。

(D-1)

まず(D-1)のコードで_Right._Refはnew A()の結果、ようするにA*が格納されているのに_Ty **_Pptr、すなわちA **にキャストされている。結果として_PptrにはAのインスタンスへのポインタがダブルポインタとして格納される。

(D-2)

(D-2)の*_Pptrは*((A **)(new A())) すなわち、Aのインスタンスの先頭4バイトが指す領域(たぶん今回はvftableが指す領域)へのポインタであるので、_Ptrにはvftableが指す領域へのポインタが設定される。注意すべきはこれがA*であるとコンパイラが認識しているという事。

(D-3)

そして、(D-3)で*_Pptrに0を代入しているが、*_PptrはAのインスタンスそのものであるため、Aのインスタンスの先頭に0を書き込んでいる。結果としてAのインスタンスの先頭にあるvftableが破壊される。

(D-4)

最後の(D-4)で、本来A*を指す_Myptrに(D-2)で設定されたvftableが指す領域へのポインタが格納される。


ようするに、auto_ptr<A>::_Myptrが変な所を指していることになる。よって、auto_ptr<A>のデストラクタがdelete _Myptrを実行したところで落ちる。

結論

結局の所、VS2005で導入されたauto_ptr_refクラスのせいで、エラーになるべき所がエラーにならないのが原因みたい。それにしても、auto_ptr_refは何がしたくて導入されたんだろう。