Я создал Qt Bugticket надеясь, что документация будет продлена.
Верить Вопрос с 2010 года и Qt Documentation, operator==()
не работает с пользовательскими типами.
Цитата:
bool QVariant :: operator == (const QVariant & v) const
Сравнивает этот QVariant с
v
и возвращаетсяtrue
если они равны; в противном случае возвращаетсяfalse
,
QVariant
использует оператор равенства типа (), который он содержит, чтобы проверить равенство.QVariant
постараюсьconvert()
v
если его тип не совпадает с типом этого варианта. УвидетьcanConvert()
для списка возможных преобразований.Предупреждение: Эта функция не поддерживает пользовательские типы, зарегистрированные с
qRegisterMetaType()
,
Я пытался воспроизвести случай репро с Stackoverflow Вопрос от 2010 и сравнение работало без проблем для меня.
Я также пошел дальше и попробовал сравнения, используя собственный класс, который также работал отлично.
Чтобы воспроизвести, поместите следующий код в любой заголовок:
enum MyEnum { Foo, Bar };
Q_DECLARE_METATYPE(MyEnum)
class MyClass
{
int value;
public:
MyClass() : value(0)
{
}
MyClass(int a) : value(a)
{
}
bool operator==(const MyClass &) const
{
Q_ASSERT(false); // This method seems not to be called
return false;
}
bool operator!=(const MyClass &) const
{
Q_ASSERT(false); // This method seems not to be called
return true;
}
};
Q_DECLARE_METATYPE(MyClass)
И следующий код в любую функцию:
QVariant var1 = QVariant::fromValue<MyEnum>(Foo);
QVariant var2 = QVariant::fromValue<MyEnum>(Foo);
Q_ASSERT(var1 == var2); // Succeeds!
var1 = QVariant::fromValue<MyEnum>(Foo);
var2 = QVariant::fromValue<MyEnum>(Bar);
Q_ASSERT(var1 != var2); // Succeeds!
QVariant obj1 = QVariant::fromValue<MyClass>(MyClass(42));
QVariant obj2 = QVariant::fromValue<MyClass>(MyClass(42));
Q_ASSERT(obj1 == obj2); // Succeeds!
obj1 = QVariant::fromValue<MyClass>(MyClass(42));
obj2 = QVariant::fromValue<MyClass>(MyClass(23));
Q_ASSERT(obj1 != obj2); // Succeeds!
Я полагаю, что в более новых версиях QT размер типа определяется, когда Q_DECLARE_METATYPE
используется для того, чтобы QVariant мог сравнивать значения неизвестных типов побайтно.
Но это всего лишь предположение, и я не хочу рисковать стабильностью моего приложения, угадывая, что делает qt, а не полагаясь на документацию.
Могу ли я узнать, как QVariant сравнивает неизвестные типы? Я бы предпочел полагаться на спецификацию, чем на реализацию.
Боюсь, вам нужно полагаться на код (и, будучи поведенческим, его нельзя изменить, не нарушая), а не на документацию. Впрочем, чуть ниже есть сюрприз.
QVariant::operator==
для типов с незарегистрированными операторами просто буду использовать memcmp
, Соответствующий фрагмент (в 5.1):
bool QVariant::cmp(const QVariant &v) const
{
QVariant v1 = *this;
QVariant v2 = v;
if (d.type != v2.d.type)
// handle conversions....
return handlerManager[v1.d.type]->compare(&v1.d, &v2.d);
}
handlerManager
это глобальный объект, который используется для выполнения манипуляций с учетом типов. Он содержит массив QVariant::Handler
объекты; каждый из таких объектов содержит указатели для выполнения определенных операций над типами, которые они умеют обрабатывать:
struct Handler {
f_construct construct;
f_clear clear;
f_null isNull;
f_load load;
f_save save;
f_compare compare;
f_convert convert;
f_canConvert canConvert;
f_debugStream debugStream;
};
Каждый из этих членов на самом деле является указателем на функцию.
Причина наличия этого массива глобальных объектов немного сложна — она позволяет другим библиотекам Qt (скажем, QtGui) устанавливать пользовательские обработчики для типов, определенных в этих библиотеках (например, QColor).
operator[]
на handlerManager
выполнит некоторую дополнительную магию, а именно получит правильный обработчик для модуля, учитывая тип:
return Handlers[QModulesPrivate::moduleForType(typeId)];
Теперь тип, конечно, пользовательский тип, поэтому возвращаемый здесь обработчик Unknown
модуль. Тот Handler
будет использовать customCompare
функция в qvariant.cpp
, который делает это:
static bool customCompare(const QVariant::Private *a, const QVariant::Private *b)
{
const char *const typeName = QMetaType::typeName(a->type);
if (Q_UNLIKELY(!typeName) && Q_LIKELY(!QMetaType::isRegistered(a->type)))
qFatal("QVariant::compare: type %d unknown to QVariant.", a->type);
const void *a_ptr = a->is_shared ? a->data.shared->ptr : &(a->data.ptr);
const void *b_ptr = b->is_shared ? b->data.shared->ptr : &(b->data.ptr);
uint typeNameLen = qstrlen(typeName);
if (typeNameLen > 0 && typeName[typeNameLen - 1] == '*')
return *static_cast<void *const *>(a_ptr) == *static_cast<void *const *>(b_ptr);
if (a->is_null && b->is_null)
return true;
return !memcmp(a_ptr, b_ptr, QMetaType::sizeOf(a->type));
}
Который, кроме небольшой проверки ошибок и обработки общих и нулевых вариантов особым образом, использует memcmp
на содержание.
… кажется, только если тип не является указателем Интересно, почему там этот код …
Начиная с Qt 5.2, вы можете использовать QMetaType::registerComparator
(увидеть Вот) чтобы вызвать Qt operator<
а также operator==
на ваш пользовательский тип. Просто добавьте в свой main
:
qRegisterMetaType<MyClass>();
QMetaType::registerComparators<MyClass>();
И вуаля, вы нажмете на assert в вашем операторе равенства. QVariant::cmp
сейчас:
QVariant v1 = *this;
QVariant v2 = v;
if (d.type != v2.d.type)
// handle conversions, like before
// *NEW IMPORTANT CODE*
if (v1.d.type >= QMetaType::User) {
// non-builtin types (MyClass, MyEnum...)
int result;
// will invoke the comparator for v1's type, if ever registered
if (QMetaType::compare(QT_PREPEND_NAMESPACE(constData(v1.d)), QT_PREPEND_NAMESPACE(constData(v2.d)), v1.d.type, &result))
return result == 0;
}
// as before
return handlerManager[v1.d.type]->compare(&v1.d, &v2.d);
Других решений пока нет …