我有一个类模板Foo<T>
,源自FooBase
.
富.h:
class FooBase
{
public:
virtual ~FooBase() {}
};
template <class T>
class Foo : public FooBase
{
public:
T t;
};
然后,我有两个翻译单元用不同T
的 Foo 实例化。用作模板参数的类T
在两个翻译单元中都被调用,但受一个未命名的命名空间保护。每个翻译单元定义一个自由函数。
测试1.h:
void test1();
测试1.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
}
测试2.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
}
最后,我有一个调用两个自由函数的主函数,并且我链接了所有三个翻译单元。
主.cpp:
#include "Test1.h"
#include "Test2.h"
int main()
{
test1();
test2();
}
问题:这是合法的 C++11 吗?
我认为应该是因为名称冲突是由未命名的命名空间解决的。但是,我现在很怀疑,因为:
- GDB(v7.7.1,在 Kubuntu 14.04 64bits 上)对此真的很困惑
- 我的真实案例中有一些奇怪的错误,我无法追踪
广发银行
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>
更糟糕的是,在检查foo.t
test1 时,它发现foo.t.x
应该只存在于 test2 中的成员!
请参阅下面的 QtCreator 中的屏幕截图:
T1
如果我在 Test1.cpp 和Test2.cpp 中命名模板参数,所有上述问题都得到解决T2
。
尽管 GDB 很混乱,但在我围绕这个最小示例尝试的所有变体中,程序似乎总是表现正确(GCC 4.8.2)。例如,通过从 base 调用的虚拟方法打印 sizeof(T) 分别正确返回test11
和4
test2 (在打印时1
,1
如果我删除未命名的命名空间,由于实际的名称冲突,我知道这会使代码不合法 C++ )。
真实案例
在我的真实案例中,我有一个段错误:
- 总是以“全局范围内的同名”出现(显然)
- “未命名命名空间下的同名”意外发生
- 从未发生过手动分配的唯一名称
我不知道这是否是因为手动分配唯一名称可以解决问题(可以吗,因为它们已经在未命名的命名空间中??),或者我的代码仍然在其他地方损坏并且我只是“幸运”(即,隐藏的未定义行为,非常可怕)。我花了两天时间试图减少一个仍然以这种方式崩溃但失败的最小示例。我只设法获得了最少的有效示例,或者我的实际代码有时会崩溃。