バックグラウンド
しばらく前に、非常に奇妙で一見間違っていると思われる動作に遭遇したため、GCC にバグ レポートを提出しました。レポートと私が得た回答は次の場所で確認できます。
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47305
(ここではそのほとんどを複製します。)
当時、私は答えを理解していませんでしたが、StackOverflow のメンバーではなく、それについて尋ねる人もいなかったので、回避策をハックして続行しました。しかし最近、私はこのコードを再検討していましたが、これがバグではない理由をまだ理解していないので...
私の質問
私の Mac (現在は OS X、Darwin 12.2.0 x86_64) に含まれている C++ stdlib ディストリビューションでは、106 ~ 116 行目の実装std::vector::erase()
が次の/usr/include/c++/4.2.1/vector.tcc
ように示されています。
template<typename _Tp, typename _Alloc>
typename vector<_Tp, _Alloc>::iterator
vector<_Tp, _Alloc>::
erase(iterator __position)
{
if (__position + 1 != end())
std::copy(__position + 1, end(), __position);
--this->_M_impl._M_finish;
this->_M_impl.destroy(this->_M_impl._M_finish);
return __position;
}
が指す要素に対して呼び出されるのではなく、this の呼び出しの前にベクトル内の最後destroy()
の要素に対して呼び出されることに注意してください。これは間違っていると思います。代わりに、 が指す要素を呼び出す必要があると思います。単純な POD 型の場合、これはそれほど大きな問題ではありませんが、デストラクタに副作用があるクラス (スマート ポインターなど) の場合は、重要になる可能性があります。erase()
__position
destroy()
__position
次のコードは、問題を示しています。
#include <vector>
#include <iostream>
class MyClass
{
int m_x;
public:
MyClass(int x) : m_x(x) { }
~MyClass()
{
std::cerr << "Destroying with m_x=" << m_x << std::endl;
}
};
int main(void)
{
std::vector<MyClass> testvect;
testvect.reserve(8);
testvect.push_back(MyClass(1));
testvect.push_back(MyClass(2));
testvect.push_back(MyClass(3));
testvect.push_back(MyClass(4));
testvect.push_back(MyClass(5));
std::cerr << "ABOUT TO DELETE #3:" << std::endl;
testvect.erase(testvect.begin() + 2);
std::cerr << "DONE WITH DELETE." << std::endl;
return 0;
}
これを Mac で g++ バージョン 4.2.1 (コマンド ライン引数なし) でコンパイルすると、実行すると次のようになります。
Destroying with m_x=1
Destroying with m_x=2
Destroying with m_x=3
Destroying with m_x=4
Destroying with m_x=5
ABOUT TO DELETE #3:
Destroying with m_x=5
DONE WITH DELETE.
Destroying with m_x=1
Destroying with m_x=2
Destroying with m_x=4
Destroying with m_x=5
「ABOUT TO DELETE #3」メッセージの後の重要な行は、私が追加した 5 番目のもの (のコピー) のためにデストラクタが実際に呼び出されたことを示していることに注意してください。 重要なことに、#3 のデストラクタは呼び出されません!!
範囲を取るバージョンerase()
(2 つの反復子) にも同様の問題があるようです。
だから私の質問は、ベクトルから消去している要素のデストラクタが呼び出されると期待するのは間違っているのでしょうか? これを当てにできないと、ベクトルでスマート ポインターを安全に使用できないようです。それとも、これは Apple が配布する STL ベクトル実装の単なるバグですか? 明らかな何かが欠けていますか?