如何支持包含自定义类型的QVariant对象的比较?

22
根据Qt文档显示,如果QVariant包含自定义类型,则QVariant::operator==运算符不会按预期工作:
bool QVariant::operator== ( const QVariant & v ) const
比较此QVariant与v,如果相等则返回true;否则返回false。对于自定义类型,它们的相等运算符不会被调用,而是比较这些值的地址。如何让此运算符对自定义类型有实际意义?在我的情况下,我将枚举值存储在QVariant中,例如:
enum MyEnum { Foo, Bar };

Q_DECLARE_METATYPE(MyEnum);

在某个函数中:

QVariant var1 = QVariant::fromValue<MyEnum>(Foo);
QVariant var2 = QVariant::fromValue<MyEnum>(Foo);
assert(var1 == var2); // Fails!

如何改变我的方法以便这个断言为真?

我知道为什么它不起作用——每个变体都存储了一个枚举值的副本,因此它们具有不同的地址。我想知道如何更改我的方法来存储这些值在变量中,以便要么这不是问题,要么两个变体引用相同的基础变量。

我认为没有可能避免需要进行等式比较。上下文是,我正在使用此枚举作为QComboBox中项目的UserData,并希望能够使用QComboBox::findData来查找对应于特定枚举值的项目索引。


其他答案:https://dev59.com/j2Ij5IYBdhLWcg3w95rA - peppe
3个回答

15
明显的答案是使用var1.value<MyEnum>() == var2.value<MyEnum>()将数据强制转换来进行比较,但这需要您在比较时知道类型。在您的情况下,似乎可能是可行的。如果您只是使用枚举,也可以将其转换为int以便在QVariant中存储。
编辑:为了澄清关于搜索QComboBox的问题,它使用组合框的模型来查找数据。具体而言,它使用QAbstractItemModelmatch()函数来检查相等性。幸运的是,这个函数是虚拟的,所以您可以在子类中重载它。

2
显而易见的答案行不通,因为我无法更改QComboBox在其findData方法内部执行的操作。是的,我知道我可以将其存储为int,但这似乎不够优雅。此外,虽然我现在正在使用枚举,但以后可能会想以相同的方式使用类,因此我正在寻找一般解决方案。 - Tyler McHenry
2
然后您可以在模型类中重写match()函数 (http://doc.qt.nokia.com/4.6/qabstractitemmodel.html#match),并将其分配给 QComboBox,因为它是如何进行 findData 比较的。编辑:请参见此处:http://qt.gitorious.org/qt/qt/blobs/4.7/src/gui/widgets/qcombobox.cpp#line1490 - Adam W
1
看起来非常有前途。如果它有效,我会回来并接受这个答案,一旦我有机会尝试它。 - Tyler McHenry
3
非常好!我使用泛型类T的子类化QStandardItemModel,并重新实现了match方法。如果正在搜索精确匹配,并且要查找的变量可以转换为T类型,则match方法将比较这些变量.value<T>()而不是它们本身。感谢! - Tyler McHenry
2
很高兴它有所帮助,不久前我意识到QComboBox可以引用QAbstractItemModel,这使得许多界面变得更容易了! - Adam W
1
我认为QComboBox是一个被低估的类。我也遇到了完全相同的问题。他们应该用大字母拼出来,因为它的命名不像其他视图类那样(QxyzView)。 - Kuba hasn't forgotten Monica

4

解决Qt 5的方案

自Qt 5.2版本起,Qt支持此功能。请参见QVariant::operator==QMetaType::registerComparators

解决Qt 4的方案

如果您仍在使用Qt 4且无法(或不想)升级到Qt 5,则可以使用我为我的一个项目编写的CustomVariantComparator类。

您可以按照以下方式使用它。假设我们有一个实现operator==并应在QVariant中使用的Foo类:

class Foo {
public:
    bool operator==(const Foo &other) { return ...; }
};
Q_DECLARE_METATYPE(Foo)

然后,只需将 Q_DEFINE_COMPARATOR 宏放在 Foo 的实现旁边(即在 Foo.cpp 文件中,而不是在 Foo.h 文件中):
Q_DEFINE_COMPARATOR(Foo)

接下来,在构建QApplication(或QCoreApplication)实例之后,启用自定义变量比较器(只需执行一次):

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    CustomVariantComparator::setEnabled(true);
    // more code...
}

现在,以下代码片段将按预期工作(即调用 Foo::operator==)。
QVariant::fromValue(Foo()) == QVariant::fromValue(Foo())

4
尝试黑掉QVariant,通过原型定义函数。
typedef bool (*f_compare)(const Private *, const Private *);

并将其设置为 QVariant 处理程序; 要使用 QVariant 和 Qt 一起工作,请使用 Handler:

struct Handler {
    f_construct construct;
    f_clear clear;
    f_null isNull;
  #ifndef QT_NO_DATASTREAM
    f_load load;
    f_save save;
 #endif
    f_compare compare;
    f_convert convert;
    f_canConvert canConvert;
    f_debugStream debugStream;
};

这个例子演示了如何修改qvariant调试输出并将其转换成字符串。这是一个非常简单的例子,你需要根据自己的问题进行扩展。"Identifier"是我的自定义类型。

class HackVariant : private QVariant
{
public:
     static void hackIt() {
         origh = handler;
         Handler* h = new Handler;
         *h = *origh;
         h->convert = convert;
         h->debugStream = hackStreamDebug;
         handler = h;
     }

private:
     static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, bool *ok)
     {
         //qDebug() << Q_FUNC_INFO << "type:" << d->type;
         if (d->type >= QVariant::UserType)
         {
             QString& str = *((QString*)result);
             Identifier* ident = (Identifier*)(constData(d));
             str = ident->toString();
         }
         else
             return origh->convert(d, t, result, ok);
         return true;
     }

     static void hackStreamDebug(QDebug dbg, const QVariant &v) {
         if (v.canConvert<Identifier>())
             dbg << v.value<Identifier>();
         else
             origh->debugStream(dbg, v);
     }

     static const Handler* origh;

     static const void *constData(const QVariant::Private *d)
     {
         return d->is_shared ? d->data.shared->ptr : reinterpret_cast<const void *>(&d->data.ptr);
     }

};

您需要创建函数并将其设置为处理程序。在使用之前,请勿忘记在main.cpp中调用HackVariant::hackIt()(var1 == var2)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接