Конфликт имен, безымянное пространство имен, шаблон класса
У меня есть шаблон класса Foo<T>
, исходя из FooBase
,
foo.h:
class FooBase
{
public:
virtual ~FooBase() {}
};
template <class T>
class Foo : public FooBase
{
public:
T t;
};
Затем у меня есть две единицы перевода, инстанцирующие Foo с разными T
"S. Класс, используемый в качестве параметра шаблона, называется 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
}
Наконец, у меня есть main, которая вызывает обе бесплатные функции, и я связываю все три единицы перевода.
main.cpp:
#include "Test1.h"
#include "Test2.h"
int main()
{
test1();
test2();
}
Вопрос: это законно C++11?
Я думал, что так и должно быть, поскольку конфликт имен разрешен безымянным пространством имен. Однако сейчас я сомневаюсь, потому что:
- GDB (v7.7.1, на Kubuntu 14.04 64bit) действительно смущен этим
- У меня есть странные ошибки в моем случае из реальной жизни, которые я не могу отследить
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>
И даже намного, намного хуже, при проверке foo.t
на test1 находит участника foo.t.x
который должен существовать только в test2!
Смотрите скриншоты ниже в QtCreator:
Все вышеперечисленные проблемы решаются, если я назову параметр шаблона T1
в Test1.cpp и T2
в Test2.cpp.
Несмотря на путаницу в GDB, во всех вариантах, которые я пробовал на этом минимальном примере, программа всегда работает правильно (GCC 4.8.2). Например, печать sizeof(T) через виртуальный метод, вызываемый из базы, корректно возвращает 1
а также 4
для test1 и test2 соответственно (пока он печатает 1
а также 1
если я удаляю безымянное пространство имен из-за фактического конфликта имен, который, как я знаю, делает код недопустимым (C++).
Случай из жизни
В моем случае из реальной жизни у меня есть ошибка, которая:
- всегда происходит с "тем же именем в глобальной области видимости" (очевидно)
- происходит непредсказуемо с "тем же именем в безымянном пространстве имен"
- никогда не встречалось с назначенными вручную уникальными именами
Я не знаю, связано ли это с тем, что назначение уникальных имен вручную решает проблему (не так ли, поскольку они уже были в безымянном пространстве имен??), или если мой код все еще не работает где-то еще, и мне просто "повезло" (т.е. скрытое неопределенное поведение, которое очень страшно). Я потратил два дня, пытаясь свести к минимуму пример, который все еще дает сбой, но не удался. Мне удалось получить только минимальные примеры работы или мой реальный код, который иногда дает сбой.