First, a little history: when I started working with Qt I was coming from a STL/Boost background and was accustomed to using the STL containers and strings. On the first Qt project that I’ve worked on, a Linux port for a large-scale C++ system, the team’s approach was to use Qt strictly for the UI and the STL, Boost and other libraries for everything else.
At that time, I didn’t even know there was a QTL and I was using std::(w)string pretty much everywhere. After working on more Qt projects, I now use the QTL everywhere and have extensive experience with both libraries. My conclusion is that there is no clear winner in this comparison, that the choice can be quite complicated depending on the project type, and switching from one library to the other poses some subtle difficulties.
The STL and Qt have a different philosophy when it comes to containers:
- The containers are very parameterizable and built for maximum speed.
- Has performance guarantees for each container.
- Each implementation can use copy-on-write as it sees fit.
- Free, generic functions are prefered to member functions.
- There are multiple container implementations, just as there are multiple standard library implementations.
- … and those implementations are not binary compatible, but they must obey the C++ standard. This includes the interfaces and performance guarantees.
- Each implementation is free to do whatever optimizations that it pleases as long as it respects the standard.
- Provides regular iterators.
- Only includes a hash in TR1.
- Only includes a variable length array in TR1.
- Have a header-only implementation and the compiler has access to all the source code.
- Has the <algorithm> header.
- Containers are not parameterizable. Don’t like the default memory allocator? Well, that’s too bad…
- Provides a list of performance guarantees in the docs.
- Use copy-on-write to avoid needless expensive copies.
- Lots of member functions provided for convenience. e.g: contains, indexOf, removeAt, takeAt, uniqueKeys, startsWith, etc
- One implementation that is binary compatible between major releases but not major versions. i.e: Qt 4.5 is compatible up to Qt 4.9, but Qt 3.2.2 is not compatible with Qt 4.0
- Qt containers do optimizations which are available on all operating systems.
- Can use STL-style iterators or Java-style iterators.
- Includes a hash and multihash implementation.
- Includes QVarLengthArray.
- Are not header only. Some functions are not available when building your program and they can’t be used for global optimizations.
- Has the <QtAlgorithm> header.
If you will use the Qt containers together with the Qt framework, you will get a few other advantages:
- Qt containers are streamable by default with QDataStream. This means that you only have to write a streaming operator for your user defined contained type.
- They are used extensively in Qt’s API. If you use STL containers you will almost certainly have to make conversions.
- Some signal slots connections copy their parameters. Since the Qt containers use COW, copying them is less costly.
- You can use Q_FOREACH with Qt containers. Since Q_FOREACH does a copy of the target container, it would be too expensive to use with the STL.
My opinion is that the biggest advantage of the QTL is that it has the same implementation (including binary compatibility) on all OSes supported by Qt. Some STL implementations might be below par when it comes to performance or they might be missing functionality. Some platforms don’t even have an STL! On the other hand, the STL is more customizable and is available in its entirety in header files… Like I said, there is no clear winner.
When deciding about using QString instead of std::(w)string, there are few points to consider:
- QString scores a lot higher than std::basic_string when it comes to usability.
- It offers Unicode support.
- It lives in a dll/so, which means that whole program optimizations won’t be available. This can be worked around by linking with Qt statically, but the static vs dynamic debate is quite complicated and a decision has wider reaching effects.
- Doesn’t allow customization through character traits or custom allocators. In fact, QString is a regular class, not a template and it can easily be forward declared.
Personally, I almost always use QString if I’m using Qt. I originally used conversion helpers to avoid a deep Qt dependency, but unless most of your program is Qt-free, it’s not worth it to use both QString and std::string.
Not a drop in replacement
Qt containers are STL-compatibile in the way that they provide iterators that match the STL algorithm library and the container interfaces mimic the STL ones. In addition to this, there’s an STL compatibility mode that allows you to directly convert to and from a STL container when you’re working with its Qt counterpart. e.g: QVector::toStdVector
Even so, you can’t swap between Qt and STL containers as easily as replacing a type definition:
- QVector::at doesn’t do range checking besides an assert.
- QList provides index-based access and is more similar to a std::deque. QLinkedList is probably the closes thing to std::list.
- There are no constBegin, constEnd, constFind, etc functions in the STL. You might not easily be able to take advantage of Qt’s COW implementation.
- The QMap and QMultiMap interfaces differ from the std::map and std::multimap ones. In particular, there’s no equal_range function.
- Qt containers can’t be parametrized with an allocator.
Thiago Maceira from Nokia/ex-Trolltech has done an excellent summary of the Qt smart pointers. If you read this summary, you’ll know 99% of what’s to know about Qt’s smart pointers.
I have added only a few observations from experience:
- Unfortunately TR1 is not as wide spread as I would like at this point and I find it much easier to use the Qt shared pointer instead of introducing a large Boost dependency in a project.
- QScopedPointer supports d-pointers and custom deleters. AFAIK, neither scoped_ptr nor auto_ptr do.