Q与自己工作的类型进行变体比较?

问题描述 投票:0回答:1

更新

我创建了一个 qt bugticket 希望文档能够得到扩展。

原始问题

相信 2010 年的问题Qt 文档

operator==()
不适用于自定义类型。

引用:

bool QVariant::operator==(const QVariant & v) const

将此 QVariant 与

v
进行比较,如果相等则返回
true
;否则返回
false

QVariant
使用其包含的 type() 的相等运算符来检查相等性。如果
QVariant
的类型与此变体的类型不同,则会尝试
convert()
v
。请参阅
canConvert()
了解可能的转换列表。

警告:此函数不支持通过

qRegisterMetaType()
注册的自定义类型。

我尝试从 2010 年的 Stackoverflow Question 中重现重现案例,并且比较对我来说没有任何问题。

我还更进一步,尝试使用自己的类进行比较,效果也很完美。 要重现,请将以下代码放入任何标头中:

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 如何比较未知类型吗?我更喜欢依赖规范而不是实现。

c++ qt qt5 qvariant
1个回答
27
投票
恐怕您需要依赖代码(并且,作为行为,它不能在不破坏的情况下进行更改),而不是文档。不过,下面有一个惊喜。

这是相关代码。

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)];
现在的类型当然是自定义类型,所以这里返回的Handler就是

Unknown

模块的Handler。 
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>();
瞧,您将在相等运算符中命中断言。 

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);
    
© www.soinside.com 2019 - 2024. All rights reserved.