[Development] C++11 decltype magic with a container?

Stephen Kelly stephen.kelly at kdab.com
Wed May 22 11:07:44 CEST 2013


On Tuesday, May 21, 2013 11:38:41 Thiago Macieira wrote:
> On terça-feira, 21 de maio de 2013 20.27.41, Stephen Kelly wrote:
> > For a Qt patch, I need to know at compile-time whether std::find can be
> > used  with it, ie, whether the value_type of the container can be
> > equality-compared.
> > 
> >  https://codereview.qt-project.org/#change,55735
> > 
> > I used a decltype, which works for the type itself, but not for the
> > container.  I also tried an enable_if, but that doesn't solve the problem
> > either.
> 
> Let's go back a step.
> 
> What are you trying to do?

It is now possible to iterate over the elements of a sequential iterator via 
the QVariant API, without knowing exactly what type of container it is:

 QVector<int> vec;
 vec.push_back(1);
 vec.push_back(7);
 vec.push_back(0);
 vec.push_back(1);
 QVariant var = QVariant::fromValue(vec);

 // ...

 if (var.canConvert<QVariantList>()) {
   QSequentialIterable iter = var.value<QSequentialIterable>();

   QSequentialIterable::const_iterator it = iter.begin();
   const QSequentialIterable::const_iterator end = iter.end();

   for ( ; it != end; ++it) {
     qDebug() << *it;
   }

 }

The problem is that QSequentialIterable::const_iterator::operator*() returns a 
'QVariant', not a 'const QVariant &', so it's not stl compatible and can't be 
used with standard algorithms such as find.

So, in order to test if a container contains a particular value without first 
iterating over it and creating a QVariantList and testing *that*, I wanted to 
make it possible to do this:

 QSequentialIterable::const_iterator it = iter.find(2);
 if (it != iter.end()) ...
 
That requires that the Container::value_type is equality comparable. So, the 
patch that I linked to uses decltype to try to check if that is possible.

struct A {};

 std::vector<A> v;
 A a;
 v.push_back(a);
 std::find(v.begin(), v.end(), a); // Doesn't compile.
 QList<A> l;
 l.push_back(a);
 l.indexOf(a); // Doesn't compile.

While my patch works for one step, of introspection, as soon as nested 
containers are used it no longer gives correct results, with the result that 
my patch does not actually compile:

/home/stephen/dev/src/qtbase/src/platformsupport/linuxaccessibility/struct_marshallers_p.h:168:1:   
required from here
../../include/QtCore/../../../../../src/qtbase/src/corelib/tools/qlist.h:752:9: 
error: no match for ‘operator==’ in ‘i-
>QList<T>::Node::t<QSpiObjectReference>() == li-
>QList<T>::Node::t<QSpiObjectReference>()’


 struct QSpiObjectReference {}; // Not equality-comparable
 typedef QPair < unsigned int, 
         QList < QSpiObjectReference > > QSpiRelationArrayEntry;
 typedef QList< QSpiRelationArrayEntry > QSpiRelationArray;

 Q_DECLARE_METATYPE(QSpiRelationArray) // line 168

QSpiRelationArray::value_type is QSpiRelationArrayEntry which, being a QPair, 
does have a operator==(), so the decltype gives the result that the std::find 
should work.

> And what types of a container aren't comparable? All containers require
> comparison in order to implement find() and constFind();

I hope the above answers your question.

Thanks,

-- 
Stephen Kelly <stephen.kelly at kdab.com> | Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
www.kdab.com || Germany +49-30-521325470 || Sweden (HQ) +46-563-540090
KDAB - Qt Experts - Platform-Independent Software Solutions
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.qt-project.org/pipermail/development/attachments/20130522/8d833180/attachment.sig>


More information about the Development mailing list