Stl or Qt Containers

STL or Qt containers?

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's foreach 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.

Generic Search Algorithms for Qt container classes

STL algorithms defined in algorithm header can be used with Qt containers. If Qt lacks an equivalent algorithm there is no reason to avoid using the STL algorithm. If Qt is built with STL support it should work by default.

#include <algorithm> // std::find_if
#include <QApplication>
#include <QVector>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QVector<int> c{ 2,3,4,6,6,15 };
if (std::find_if(c.begin(), c.end(), [](const int& value) { return value % 5 == 0; }) != c.end()) {
...
}
return app.exec();
}

How to use STL algorithms in Qt?

This function locates in std namespace, so just write:

#include <QApplication>
#include <algorithm>
#include <QVector>
using namespace std;//new line!

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QVector<int>vec{9,6,10,5,7};
sort(vec.begin(),vec.end());
return a.exec();
}

Or write std::sort every time:

#include <QApplication>
#include <algorithm>
#include <QVector>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QVector<int>vec{9,6,10,5,7};
std::sort(vec.begin(),vec.end());
return a.exec();
}

Using STL algorithms with Qt containers

std::copy_if increments the output iterator when copying elements. You passed it searchResult.begin() which is equally an end() iterator since searchResult is an empty container. And increment(ing) an iterator passed the end() iterator invokes Undefined Behavior.

since QList<T> supports a push_back(T) member function, you should use std::back_inserter to create a std::back_insert_iterator that will be doing push back's to searchResult

std::copy_if(model->getStudentEntryList().begin(),
model->getStudentEntryList().end(),
std::back_inserter(searchResult),
condition);

How to fill STL containers by means of generate_n with index increment

You can move the index into the lambda capture and make the lambda mutable like this (requires C++14):

std::generate_n(std::back_inserter(lst), N,
[&func, idx = -1] () mutable {idx++; return func(idx);});

Now you can omit the line int idx = -1;. There might be a better solution though, as sacrificing the default const qualification of the closure just to move an integer declaration from the surrounding scope into the capture isn't perfect. Still though, the scope of idx has been reduced, and if I understand your question correctly, this was the goal.

Why use QVector(Qt) instead of std::vector

This article loooks good. It compares Qt Template Library with Standard Template Library:

  • QTL vs STL

Hope, you'll find it interesting seeing all the differences listed there in the article.

EDIT:

Here is what I find interesting:

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
.

Like he said, no clear winner. But still reading the article makes lots of things clear. Its better to know the difference than going for one, without knowing the other.

Is there still a need to provide default constructors to use STL containers?

This quote is from the C++ Programming Language, Special edition , 2005 by Bjarne Stroustrup in section 16.3.4:

If a type does not have a default constructor, it is not possible to create a vector with elements of that type, without explicitly providing the value of each element.

So it was indeed a standard requirement. It was also required that (section 17.1.4) :

To be an element of a container, an object must be of a type that allows the container implementation to copy it. The container may copy it using a copy constructor or an assignment; in either case the result of the copy must be an equivalent object.

So yes, there were "official" constructor requirementsand the library implementation were supposed to be interchangeable and not add other requirements. (Already in the very first proposal for STL in 1995, the authors tried as much as possible to clearly indicate specifications and narrow down the implementation dependent flexibility.)

You therefore had to provide a default constructor in the case where you declared other constructors:

If a user has declared a default constructor, that one will be used; otherwise, the compiler will try to generate one if needed and if the user hasn't declared other constructors.

Nowadays, this requirement is relaxed. Since C++11:

The requirements that are imposed on the elements depend on the actual operations performed on the container.

So you can define a vector for a class without default constructor, if it doesn't make sense. This for example perfectly works (online demo):

class MyClass {
public:
MyClass(int x) {}
};
int main() {
vector<MyClass> v;
MyClass test{1};
v.push_back(test);
}

But it works only as long as you don't use any operation that would need the default constructor. For instance v.resize(6); would fail to compile.

C++ STL: Why allocators don't increase memory footprint of containers?

Your allocator is not being used.

By default, std::set receives std::allocator<int>, but it needs to allocate some kind of nodes, not ints. It uses std::allocator_traits::rebind to get a different allocator for its internal node type.

Pre-C++20 std::allocator has a rebind member type, which you inherit, and which std::allocator_traits::rebind finds. That rebind points to std::allocator, so that's what you get.

Starting from C++20, there's no rebind in std::allocator, so std::allocator_traits::rebind falls back to directly modifying the first template parameter of your allocator, and since it's not a template, you get a compilation error.

A possible solution is to make your allocator a template, and to provide your own rebind (which can be malformed, then the template parameter will be replaced automatically):

template <typename T>
struct MyAllocator : public std::allocator<T>
{
char dummy[1024];
struct rebind {}; // Malformed `rebind` to hide the inherited one, if any.
};

Then 1072 is printed for me.



Related Topics



Leave a reply



Submit