STL or Qt containers?
What are the pros and cons of using Qt containers (QMap
, QVector
, etc.) over their STL equivalent?
I can see one reason to prefer Qt:
- Qt containers can be passed along to other parts of Qt. For example, they can be used to populate a
QVariant
and then aQSettings
(with some limitation though, onlyQList
andQMap
/QHash
whose keys are strings are accepted).
Is there any other?
Edit: Assuming the application already relies on Qt.
This is a difficult to answer question. It can really boil down to a philosophical/subjective argument.
That being said...
I recommend the rule "When in Rome... Do as the Romans Do"
Which means if you are in Qt land, code as the Qt'ians do. This is not just for readability/consistency concerns. Consider what happens if you store everything in a stl container then you have to pass all that data over to a Qt function. Do you really want to manage a bunch of code that copies things into/out-of Qt containers. Your code is already heavily dependent on Qt, so its not like you're making it any more "standard" by using stl containers. And whats the point of a container if everytime you want to use it for anything useful, you have to copy it out into the corresponding Qt container?
I started by using std::(w)string
and the STL containers exclusively and converting to/from the Qt equivalents, but I have already switched to QString
and I find that I'm using Qt's containers more and more.
When it comes to strings, QString
offers much more complete functionality compared to std::basic_string
and it is
completely Unicode aware. It also offers an efficient COW implementation, which I've come to rely on heavily.
Qt's containers:
- offer the same COW implementation as in
QString
, which is extremely useful when it comes to using Qt'sforeach
macro (which does a copy) and when using meta-types or signals and slots. - can use STL-style iterators or Java-style iterators
- are streamable with
QDataStream
- are used extensively in Qt's API
- have a stable implementation across operating systems. A STL implementation must obey the C++ standard, but
is otherwise free to do as it pleases (see the
std::string
COW controversy). Some STL implementations are especially bad. - provide hashes, which are not available unless you use TR1
The QTL has a different philosophy from the STL, which is well summarized by J. Blanchette: "Whereas STL's containers are optimized for raw speed, Qt's container classes have been carefully designed to provide convenience, minimal memory usage, and minimal code expansion."
The above link provides more details about the implementation of the QTL and what optimizations are used.
The Qt containers are more limited than the STL ones. A few examples of where the STL ones are superior (all of these I have hit in the past):
- STL is standardized, doesn't change with every Qt version (Qt 2 had
QList
(pointer-based) andQValueList
(value-based); Qt 3 hadQPtrList
andQValueList
; Qt 4 now hasQList
, and it's nothing at all likeQPtrList
orQValueList
). Qt 6 will have aQList
that'sQVector
whileQVector
will be deprecated. Even if you end up using the Qt containers, use the STL-compatible API subset (ie.push_back()
, notappend()
;front()
, notfirst()
, ...) to avoid porting yet again come Qt 6. In both Qt2->3 and Qt3->4 transitions, the changes in the Qt containers were among those requiring the most code churn. I expect the same for Qt5->6. - STL bidirectional containers all have
rbegin()
/rend()
, making reverse iteration symmetric to forward iteration. Not all Qt containers have them (the associative ones don't), so reverse iteration is needlessly complicated. - STL containers have range-
insert()
from different, but compatible, iterator types, makingstd::copy()
much less often needed. - STL containers have an
Allocator
template argument, making custom memory management trivial (typedef required), compared with Qt (fork ofQLineEdit
required fors/QString/secqstring/
). EDIT 20171220: This cuts Qt off of advances in allocator design following C++11 and C++17, cf. e.g. John Lakos' talk (part 2). - There's no Qt equivalent to
std::deque
. -
std::list
hassplice()
. Whenever I find myself usingstd::list
, it's because I needsplice()
. -
std::stack
,std::queue
properly aggregate their underlying container, and don't inherit it, asQStack
,QQueue
do. -
QSet
is likestd::unordered_set
, not likestd::set
. -
QList
is a just weird.
Many of the above could be solved quite easily in Qt, but the container library in Qt seems to experience a lack of development focus at the moment.
EDIT 20150106: After having spent some time trying to bring C++11-support to Qt 5 container classes, I have decided that it's not worth the work. If you look at the work that is being put into C++ standard library implementations, it's quite clear that the Qt classes will never catch up. We've released Qt 5.4 now and QVector
still doesn't move elements on reallocations, doesn't have emplace_back()
or rvalue-push_back()
... We also recently rejected a QOptional
class template, waiting for std::optional
instead. Likewise for std::unique_ptr
. I hope that trend continues.
EDIT 20201009: Come Qt 6, they will again rewrite their containers in incompatible ways:
-
QVector
will be renamedQList
, so you lose stabiliy-of-reference when usingQList
. -
QVector
(the name) will be deprecated.QLinkedList
will be removed. -
QHash
andQSet
are now Open-Addressing Hash Tables, also losing stability-of-reference guarantees -
QMap
will be backed bystd::map
, possibly changing insertion behaviour and, forQMultiMap
, order of equivalent elements. - Qt container sizes and indexes will become
qsizetype
(more or lessstd::ptrdiff_t
) (was:int
).
So, if you want to rewrite your container-using code, then go ahead with the Qt containers. Everyone else enjoys decades of stability with the STL containers.