Шаблонная функция C++ и прямые объявления
Я работаю над кодом, который компилирует и связывает (и даже выпускает коммерческие продукты) для Windows, используя MSVC. Это не компилируется с GCC, но я получаю следующие ошибки:
.../CBaseValue.h: In member function 'bool CBaseValue::InstanceOf()':
.../CBaseValue.h:90:18: error: invalid use of incomplete type 'struct CValueType'
.../CBaseValue.h:11:7: error: forward declaration of 'struct CValueType'
CBaseValue.h
class CValueType;
class CBaseValue {
public:
...
template <typename _Type>
bool InstanceOf() {
CValueType* pType = GetType();
if(pType == NULL) {
return false;
}
else {
return pType->IsDerivedFrom<_Type>();
}
}
...
}
CValueType.h
class CValueType : public CBaseValue {
public:
...
template <typename _Type>
bool IsDerivedFrom() {
return IsDerivedFrom(_Type::TYPEDATA);
}
...
}
Я понимаю, почему это проблема. Базовый класс (CBaseValue) имеет шаблонную функцию, которая использует производный класс (в данном случае CValueType).
Похоже, что MSVC здесь не совсем соответствует спецификации C++, и я только что ее укусил. Но поведение MSVC использования прямого объявления до тех пор, пока код, вызывающий шаблонную функцию, не будет фактически скомпилирован, также более желательно прямо сейчас. Кто-нибудь знает обходной путь, где я могу заставить этот код работать с GCC без необходимости переписывать много базового кода?
Из моего собственного исследования похоже, что передача '-fno-implicit-templates' в g++ могла бы помочь, но тогда мне нужно было бы явно определить вызываемые типы шаблонов. Их много, поэтому, если я смогу избежать этого, я бы предпочел это. Если общее мнение таково, что это мой лучший вариант... пусть будет так!
И если кому-то интересно, я портирую код на Mac, поэтому мы сейчас используем GCC.
2 ответа
Стандарт плохо сформулирован, но диагностика не требуется. MSVC прекрасно не диагностирует этот конкретный случай (даже когда происходит инстанцирование!).
В частности, Стандартные правила (C++03) в 14.6/7
Если тип, используемый в независимом имени, является неполным в момент, когда шаблон определен, но завершен в момент, когда выполняется создание экземпляра, и если полнота этого типа влияет на то, является ли программа хорошо формируется или влияет на семантику программы, программа плохо сформирована; Диагностика не требуется.
Таким образом, решение состоит в том, чтобы просто сделать тип зависимым, но организовать его так, чтобы во время создания экземпляра этот тип был обозначен. Например, вы можете сделать это, переписав свой шаблон следующим образом
template<typename T, typename> // just ignore second param!
struct make_dependent { typedef T type; };
template <typename Type> // eww, don't use "_Type" in user code
bool InstanceOf() {
typename make_dependent<CValueType, Type>::type* pType = GetType();
// ...
return pType->template IsDerivedFrom<Type>();
// ...
}
Кажется, что CBaseValue::InstanceOf()
Функция бесполезна для всех, кроме CValueType.h.
Так что ждите, чтобы предоставить определение, пока все необходимые типы не доступны. (РЕДАКТИРОВАТЬ: Это именно то, что предлагается в комментарии Чарльза Бейли, который он разместил, когда я печатал, - я думаю, мы думаем одинаково.)
CBaseValue.h
class CValueType;
class CBaseValue {
public:
...
template <typename _Type>
bool InstanceOf();
...
}
CValueType.h
class CValueType : public CBaseValue {
public:
...
template <typename T>
bool IsDerivedFrom() {
return IsDerivedFrom(T::TYPEDATA);
}
...
}
template <typename T>
inline bool CBaseValue::InstanceOf() {
CValueType* pType = GetType();
if(pType == NULL) {
return false;
}
else {
return pType->IsDerivedFrom<T>();
}
}
Они кажутся очень тесно связанными, поэтому, возможно, было бы лучше иметь только один заголовочный файл для обоих классов или один общедоступный заголовочный файл, включающий отдельные заголовки в правильном порядке.