いさぽん.COM「つくる」に挑戦中

ゲーム系プログラマによる特に方針のないブログ。技術系とカレー、ラーメンネタ多めだったはずが、最近はダイエットネタ多め。

C++ 同じ名前で別の構造体は宣言するべからず

実験をしていたらおかしなことになった

テンプレートの勉強というかあれこれ実験をしていたのだけど、突然意図しない動作をしだしたので原因と解決方法をメモ。 いや、気が付かないよ。こんなん。

症状

なぜかデストラクタが呼ばれない(デストラクタ以外も意図しない挙動をする)。

次のような簡単なプログラムを作ってみます。foo(new test()); とか書くとデストラクタを呼ぶだけの簡単なもの。 std::allocator::destroy() みたいなもんです。あくまでも実験なのでこのプログラムに対して意味はありません。

`` struct test { ~test() { std::cout << “~test()” << std::endl; } };

template struct foo { void destroy(T* p) { p->~T(); operator delete(_p); } };

// main() から呼ばれる実験プログラムを実行する関数 void experimental_2() { foo(new test()); }

main関数から experimental_2() を呼び出すので、コンソールに ~test() という文字が出るはずだが、なぜか出ない。clang++だと意図通りに ~test() という文字列が出る。

## 原因

リンクしている別のコードに test というクラスがあった。例えば以下のコード。空っぽの test という構造体です。

``
struct test
{
};

// main() から呼ばれる実験プログラムを実行する関数
void experimental_1()
{
}

msvcはリンク時に static や namespace {} (無名の名前空間) ではない構造体や関数で同一のものがあれば「中身は同じもののはずである」としてリンク時に関数を結合したりしてくれます。

そのおかげで、先の experimental_2() で最終的に呼び出されている test::~test() は exprtimental_1() の書かれているソースファイルに存在する省略された test::~test() が呼ばれているわけです。

解決方法

同じ名前の構造体やらクラスを作らない。いや、本来当たり前のことなんだけど。 もし特定のソースファイル内のローカルなものにするならしっかり無名の名前空間に閉じ込めるなりしましょう。