我有一个类模板Foo<T>
,派生自FooBase
。
foo.h中:
class FooBase
{
public:
virtual ~FooBase() {}
};
template <class T>
class Foo : public FooBase
{
public:
T t;
};
然后,我有两个翻译单元用不同的T
来实例化Foo。用作模板参数的类在两个转换单元中都称为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();
测试2.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
}
最后,我有一个调用自由函数的main,并且我链接了所有三个翻译单元。
main.cpp中:
#include "Test1.h"
#include "Test2.h"
int main()
{
test1();
test2();
}
问题:这是合法的C ++ 11吗?
我认为应该是因为名称冲突由未命名的命名空间解决。但是,我现在有疑问,因为:
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
并且无法确定base
实际上是动态类型Foo
,因此检查其成员t
。而且,而不是好的
<vtable for Foo<(anonymous namespace)::T>+16>
我得到test1,我在test2上得到以下内容:
<_ZTV3FooIN12_GLOBAL__N_11TEE+16>
甚至更糟糕的是,在test1上检查foo.t
时,它会找到应该只存在于test2中的成员foo.t.x
!
请参阅下面的QtCreator中的屏幕截图:
如果我在Test1.cpp中命名模板参数T1
并在Test2.cpp中命名T2
,则会解决以上所有问题。
尽管存在GDB混淆,但在我尝试围绕这个最小示例的所有变体上,程序似乎总是表现正常(GCC 4.8.2)。例如,通过从base调用的虚拟方法打印sizeof(T)正确地分别返回1
和4
for test1和test2(如果我删除它会打印1
和1
未命名的命名空间,由于实际的名称冲突,我知道这使得代码不合法C ++)。
现实案例
在我的真实案例中,我有一个段错误:
我不知道是不是因为分配唯一名称会手动修复问题(可能是因为它们已经在未命名的命名空间中了),或者我的代码在其他地方仍然被破坏了我和我#39;我只是&#34;幸运&#34; (即隐藏的未定义行为,这是非常可怕的)。我花了两天的时间试图减少一个仍然以这种方式崩溃的最小例子,但失败了。我只设法得到最小的工作示例,或者我的实际代码有时会崩溃。