У меня есть шаблон класса 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
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 ++).
Случай из жизни
В моем случае из реальной жизни у меня есть ошибка, которая:
Я не знаю, связано ли это с тем, что назначение уникальных имен вручную решает проблему (не так ли, поскольку они уже были в безымянном пространстве имен ??), или если мой код все еще не работает где-то еще, и мне просто «повезло» (т.е. скрытое неопределенное поведение, которое очень страшно). Я потратил два дня, пытаясь свести к минимуму пример, который все еще дает сбой, но не удался. Мне удалось получить только минимальные примеры работы или мой реальный код, который иногда дает сбой.
Задача ещё не решена.
Других решений пока нет …