Различить регистр для параметра t
типа T
используя SFINAE, я хочу знать, если заявление
QVariant::fromValue(t);
и / или
QVariant::value<T>();
компилирует. Если один компилируется, другой тоже, если вы не взломали систему мета-типов. Они компилируются тогда и только тогда, когда T
был объявлен с использованием Q_DECLARE_METATYPE(T)
,
Очень простой пример использования, где нужно напечатать тип значения, просто выполнив qDebugging эквивалентного обертки варианта, если и только если он поддерживается системой мета-типов (мне это не нужно, но это показывает проблему в минимальной пример):
template<class T> // enable if T NOT registered in the Qt meta type system
void print(const T &t) {
qDebug() << t;
}
template<class T> // enable if T registered in the Qt meta type system
void print(const T &t) {
qDebug() << QVariant::fromValue<T>();
}
Я знаю несколько (хотя и схожих) возможностей сделать это, но все они вводят некоторые вспомогательные структуры, сложные enable_if и т. Д. Теперь я знаю, что есть QTypeInfo
который, я думаю, уже обеспечивает что-то вроде черты типа «объявлено в системе метаданных Qt». Тем не менее, этот класс не документирован, и поэтому не рекомендуется использовать его в долгосрочном и производительном коде, так как он может меняться между версиями Qt.
Есть ли очень простой способ (проще, чем с помощью «проверки» + enable_if), чтобы проверить в специализации SFINAE, если тип T
поддерживается QVariant?
Обратите внимание, что решение все еще должно быть портативный между разными версиями Qt (Qt4 и Qt5 могут использовать разные QTypeInfo
определение). Тем не менее, я использую C ++ 11, поэтому у меня есть доступ к std::enable_if
например.
«Непереносимый» способ заключается в использовании внутреннего определения QMetaTypeId2<T>::Defined
в enable_if
(это значение перечисления, определенное как 0 или 1). Таким образом, рабочим решением будет:
template<class T>
typename std::enable_if<!QMetaTypeId2<T>::Defined>::type
print(const T &t) {
qDebug() << t;
}
template<class T>
typename std::enable_if<QMetaTypeId2<T>::Defined>::type
print(const T &t) {
qDebug() << QVariant::fromValue<T>();
}
Тем не менее, так как QMetaTypeId2
не документирован и только внутренний материал, он не должен появляться в коде клиента.
Вы должны объявить обертку, которая поможет вам. Лучшее, что я могу думать, — это иметь несколько определений, основанных на версия 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);
}
Вы можете захотеть узнать больше о СФИНАЕ. Также вы можете читать qmetatype.h
Вот.
Других решений пока нет …