gpt4 book ai didi

c++ - QVariant 与自己类型的比较工作?

转载 作者:可可西里 更新时间:2023-11-01 15:41:17 25 4
gpt4 key购买 nike

更新

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

原始问题

相信 Question from 2010Qt Documentation , operator==() 不适用于自定义类型。

引用:

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

Compares this QVariant with v and returns true if they are equal; otherwise returns false.

QVariant uses the equality operator of the type() it contains to check for equality. QVariant will try to convert() v if its type is not the same as this variant's type. See canConvert() for a list of possible conversions.

Warning: This function doesn't support custom types registered with qRegisterMetaType().

我试图从 Stackoverflow Question from 2010 中重现 repro 案例比较对我来说没有任何问题。

我还更进一步,尝试使用自己的类进行比较,该类也运行良好。要重现,请将以下代码放入任何 header 中:

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)];

现在类型当然是自定义类型,所以这里返回的 Handler 就是 Unknown 中的那个。模块。那Handler将使用 customCompareqvariant.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 (参见 here )使 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);

关于c++ - QVariant 与自己类型的比较工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19703835/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com