Ответ 1
Вы должны объявить обертку, которая поможет вам. Лучшее, что я могу представить, это иметь несколько определений, основанных на version Qt:
template<typename T>
struct is_registered
{
enum
{
value =
#if QT_VERSION >= 0x050000 // Qt 5.0.0
QMetaTypeId2<T>::Defined
#elif QT_VERSION >= 0x040000 // Qt 4.0.0
QMetaTypeId2<T>::Defined
#endif
};
};
Это не эстетическое, но это функциональное, и в вашем коде вы можете использовать is_registered<T>::value
, не беспокоясь о версии Qt. Кроме того, у меня нет Qt5 на данный момент, поэтому я не могу сказать вам, является ли QMetaTypeId2<T>::Defined
правильным для него (хотя я думаю, что это так).
Невозможно использовать qMetaTypeId<T>()
, чтобы проверить, зарегистрирован ли тип. На самом деле выражение qMetaTypeId<T>()
всегда действует независимо от типа. Если он не зарегистрирован, тело функции не будет компилироваться (точнее: в Qt 4 и 5 (на данный момент), qMetaTypeId<T>()
вызывает только другую функцию, которая не компилируется, если тип не зарегистрирован., вы не можете использовать SFINAE для его проверки. В результате код leemes дал в своем (теперь удаленном) ответе не будет работать как ожидалось.
Код был:
struct _test_is_declared_metatype
{
template<class T>
static auto test(T* t) -> decltype(qMetaTypeId<T>(), std::true_type());
static std::false_type test(...);
};
template<class T>
struct is_declared_metatype : decltype(_test_is_declared_metatype::test<T>(0))
{
};
Почему это не сработает? Цель состояла в том, что, поскольку вызов qMetaTypeId<T>()
в незарегистрированном типе приводит к ошибке компиляции, "SFINAE исключает первую функцию, если тип не зарегистрирован". Проблема здесь в том, что qMetaTypeId<T>()
всегда является допустимым выражением, поэтому qMetaTypeId<T>(), std::true_type()
тоже, и decltype(qMetaTypeId<T>(), std::true_type())
отлично определен (со значением std::true_type
).
Это связано с тем, что ошибка компиляции qMetaTypeId<T>()
возникает в теле функции, а не в ее прототипе (к примеру, код будет компилироваться только в том случае, если функция в decltype
объявлена и правильно вызвана, то есть без аргументов шаблона для например, функция без шаблона).
Таким образом, поскольку эта перегрузка test()
более конкретна, чем вариационная, она всегда выбирается, поэтому она всегда будет "возвращать", что тип зарегистрирован; вы можете увидеть его в следующем тестовом коде:
// ----------------------------------------------------------
// qmetatype.h simplification -------------------------------
// ----------------------------------------------------------
template<typename T>
struct metatype
{
enum { defined = 0 };
};
template<typename T>
struct metatype2
{
enum { defined = metatype<T>::defined };
static inline int id() { return metatype<T>::id(); }
};
template <typename T>
inline int metatypeId(
T * /* dummy */ = 0
)
{
return metatype2<T>::id();
}
#define register_meta_type( _type_ ) \
template<> \
struct metatype< _type_ > \
{ \
enum { defined = 1 }; \
static int id() \
{ \
/* Run-time registration in Qt */ \
return __COUNTER__; \
}; \
};
// ----------------------------------------------------------
// ----------------------------------------------------------
// ----------------------------------------------------------
class TestA {};
register_meta_type(TestA)
class TestB {};
class TestC {};
register_meta_type(TestC)
class TestD {};
#include <type_traits>
struct _test_is_declared_metatype
{
/*
metatypeId<T>() is always a valid expression. So this overload is
always taken
*/
template<class T>
static auto test(T* t) -> decltype(metatypeId<T>(), std::true_type());
static std::false_type test(...);
};
template<class T>
struct is_declared_metatype : decltype(_test_is_declared_metatype::test<T>(0))
{
};
#include <iostream>
#define PRINT_DEF( _type_ ) std::cout << #_type_ << " registered ? " << is_declared_metatype< _type_ >::value << "\n";
int main()
{
std::cout << std::boolalpha;
PRINT_DEF(TestA);
PRINT_DEF(TestB);
PRINT_DEF(TestC);
PRINT_DEF(TestD);
}
Вы можете захотеть узнать больше о SFINAE. Кроме того, вы можете прочитать qmetatype.h
здесь.