C++ QVariant是否与自己的类型进行比较? 更新

C++ QVariant是否与自己的类型进行比较? 更新,c++,qt,qt5,qvariant,C++,Qt,Qt5,Qvariant,我已经创建了一个文档,希望文档能够扩展 原始问题 相信一个和,操作符==()不能处理自定义类型 引述: bool QVariant::operator==(const QVariant&v)const 将此QVariant与v进行比较,如果相等,则返回true;否则返回false QVariant使用它包含的类型()的相等运算符检查相等性QVariant将尝试convert()v如果其类型与此变量的类型不同。有关可能的转换列表,请参见canConvert() 警告:此函数不支持使用qRegist

我已经创建了一个文档,希望文档能够扩展

原始问题 相信一个和,
操作符==()
不能处理自定义类型

引述:

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

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

QVariant
使用它包含的类型()的相等运算符检查相等性
QVariant
将尝试
convert()
v
如果其类型与此变量的类型不同。有关可能的转换列表,请参见
canConvert()

警告:此函数不支持使用
qRegisterMetaType()
注册的自定义类型

我试着从中复制repo案例,比较对我来说没有任何问题

我还更进一步,尝试使用自己的类进行比较,该类也工作得很好。 要复制,请将以下代码放入任何标题中:

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!
QVariant var1=QVariant::fromValue(Foo);
QVariant var2=QVariant::fromValue(Foo);
Q_ASSERT(var1==var2);//成功!
var1=QVariant::fromValue(Foo);
var2=QVariant::fromValue(Bar);
Q_ASSERT(var1!=var2);//成功!
QVariant obj1=QVariant::fromValue(MyClass(42));
QVariant obj2=QVariant::fromValue(MyClass(42));
Q_ASSERT(obj1==obj2);//成功!
obj1=QVariant::fromValue(MyClass(42));
obj2=QVariant::fromValue(MyClass(23));
Q_断言(obj1!=obj2);//成功!
我猜在较新的qt版本中,当使用
Q\u DECLARE\u元类型时,类型的大小会被获取,因此QVariant可以按字节比较未知类型的值

但这只是一个猜测,我不想因为猜测qt做了什么而不依赖文档,从而危及应用程序的稳定性


我能找出QVariant是如何比较未知类型的吗?我更喜欢依赖规范,而不是实现。

我担心您需要依赖代码(而且,作为行为,它不能在不破坏的情况下更改),而不是文档。不过,下面有一个惊喜

这是相关代码。
QVariant::operator==
对于具有未注册运算符的类型,将只使用
memcmp
。相关代码段(在5.1中)如下所示:

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)为那些LIB(f.i.QColor)中定义的类型安装自定义处理程序

handlerManager
上的
操作符[]
将执行一些额外的魔法,即在给定类型的情况下获得每个模块的正确处理程序:

return Handlers[QModulesPrivate::moduleForType(typeId)];
现在类型当然是自定义类型,因此这里返回的处理程序是
Unknown
模块的处理程序。该
处理程序
将使用
qvariant.cpp
中的
customCompare
函数,该函数执行以下操作:

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));
}
瞧,你会在等式操作符中点击断言<代码>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);

您是否在
MyEnum
MyClass
上调用了
qRegisterMetaType()
?不,我没有调用
qRegisterMetaType()
。除了我上面发布的代码行之外,没有使用MyClass或MyEnum的代码行。您是否尝试将值转换回其原始类型bevore comparing?(
QVariant::value()
)例如,
var1
的类型是什么?(或者更清楚地说,变量表示的是什么类型)
您是否尝试将值转换回其原始类型bevore comparing?(QVariant::value())
这正是我想要避免的。我正在编写一个属性小部件,我希望能够比较多个对象之间的属性,以了解要启用哪些小部件,多个选定对象是否具有相同的值。。。因此,在我的用例中,使用一种抽象的方法来比较值是非常可取的。您需要比较两个值,而您的运算符没有被调用。。。因此,出于某种原因,
QVariant
做了正确的事情,你可以尝试找出wat正在发生的事情,或者与场景背后的黑魔法一起生活伟大而非常有用的答案!谢谢太神了我认为,如果Qt诞生于今天,它将更广泛地使用模板。但编译器的标准遵从性并不像今天那样广泛(即信号/插槽机制)。
qRegisterMetaType<MyClass>();
QMetaType::registerComparators<MyClass>();
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);