Foo<T>
から派生したクラステンプレートがありFooBase
ます。
フー.h:
class FooBase
{
public:
virtual ~FooBase() {}
};
template <class T>
class Foo : public FooBase
{
public:
T t;
};
次に、異なる で Foo をインスタンス化する 2 つの翻訳単位がありますT
。テンプレート パラメーターとして使用されるクラスはT
、両方の翻訳単位で呼び出されますが、名前のない名前空間によって保護されています。各翻訳単位は自由関数を定義します。
Test1.h:
void test1();
Test1.cpp:
#include "Test1.h"
#include "Foo.h"
#include <cassert>
namespace { class T {}; }
void test1()
{
Foo<T> foo;
FooBase & base = foo;
assert(&base == &foo); // To be able to breakpoint here
}
Test2.h:
void test2();
Test2.cpp:
#include "Test2.h"
#include "Foo.h"
#include <cassert>
namespace { class T { int x; }; }
void test2()
{
Foo<T> foo;
FooBase & base = foo;
assert(&base == &foo); // To be able to breakpoint here
}
最後に、両方の free 関数を呼び出す main を用意し、3 つの翻訳単位すべてをリンクします。
main.cpp:
#include "Test1.h"
#include "Test2.h"
int main()
{
test1();
test2();
}
質問: これは合法的な C++11 ですか?
名前の競合は名前のない名前空間によって解決されるため、そうあるべきだと思いました。ただし、次の理由により、私は今疑問に思っています。
- GDB (v7.7.1、Kubuntu 14.04 64 ビット) はこれについて本当に混乱しています
- 実際のケースには追跡できない奇妙なバグがあります
GDB
GDB は test2() で次の警告を発行します。
can't find linker symbol for virtual table for `FooBase' value
can't find linker symbol for virtual table for `Foo<(anonymous namespace)::T>' value
And はbase
実際に動的な型Foo
であると判断できないため、その member を調べますt
。また、素敵な代わりに
<vtable for Foo<(anonymous namespace)::T>+16>
これは test1 で得られ、test2 では次のようになります。
<_ZTV3FooIN12_GLOBAL__N_11TEE+16>
さらに悪いことに、foo.t
test1 を検査foo.t.x
すると、test2 にのみ存在するはずのメンバーが検出されます。
QtCreator 内のスクリーンショットについては、以下を参照してください。
T1
Test1.cpp と Test2.cpp でテンプレート パラメーターに名前を付けると、上記の問題はすべて解決されますT2
。
GDB の混乱にもかかわらず、この最小限の例で試したすべてのバリアントで、プログラムは常に正しく動作するようです (GCC 4.8.2)。たとえば、 base から呼び出された仮想メソッドを介して sizeof(T) を印刷すると、 test1 と test2 に対してそれぞれ正しく返さ1
れ4
ます (印刷中に名前のない名前空間を削除する1
と1
、実際の名前の競合が原因で、コードが合法的な C++ ではないことがわかっています)。 )。
実際のケース
私の実際のケースでは、次のセグメンテーション違反があります。
- 常に「グローバルスコープで同じ名前」で発生します(明らかに)
- 「名前のない名前空間の下の同じ名前」で予期せず発生します
- 手動で割り当てられた一意の名前では発生しませんでした
一意の名前を手動で割り当てることで問題が修正されたからなのか (名前のない名前空間に既にあったので修正できるのでしょうか??)、それとも私のコードがまだ別の場所で壊れていて、私が単に「運が良かった」だけなのかはわかりません (つまり、非常に恐ろしい隠れた未定義の動作です)。この方法でまだクラッシュするが失敗する最小限の例に減らすために 2 日間を費やしました。動作する最小限の例、または時々クラッシュする実際のコードしか取得できませんでした。