C++0x in Qt

While many are so enthusiastic about QML and javascript technology, a few of us still code in C++ ;-). C++ is about to get an upgrade: C++11 (formely known as C++0x). The final draft was aproved last march in the C++ standard commitee, and the final specification is expected to be published this summer. If you don't know it yet, I invite you to read specialized pages, such as Wikipedia or C++0x FAQ

One of the goals of C++0x is supposed to make the language easier. Which is hardly possible since it only adds more stuff to learn, but with the hope that the "mosty used" subset of C++ should be easier and more intuitive.

The good news is that you do not need to wait to use it. You can start today. The compiler you use probably already supports some of C++0x: GCC or MSVC 2010

While you can already use C++0x with older Qt version such as Qt 4.7, Qt 4.8 will come with more support for some of the new C++0x constructs:

New Macros

Some macros are defined if the compiler supports the new features

Q_COMPILER_RVALUE_REFS
Q_COMPILER_DECLTYPE
Q_COMPILER_VARIADIC_TEMPLATES
Q_COMPILER_AUTO_TYPE
Q_COMPILER_EXTERN_TEMPLATES
Q_COMPILER_DEFAULT_DELETE_MEMBERS
Q_COMPILER_CLASS_ENUM
Q_COMPILER_INITIALIZER_LISTS
Q_COMPILER_LAMBDA
Q_COMPILER_UNICODE_STRINGS

Initializer lists
Qt 4.8 adds new constructors to QVector, QList, and QStringList which enable you to initialize them using the bracket initializer.
You can now do

QVector<int> testsData { 1, 2, 10, 42, 50, 123  };
QStringList options = { QLatin1String("foo"), QLatin1String("bar")  };

which initializes the containers with those element.

Rvalues references and move semantics

Most of Qt classes are implicitly shared, meaning it is efficient to copy them if you do not modify them (copy on write). This is not the case for std::vector, where each copy implies copying all the data.
So if you have code like

std::vector<int> m_foo;
...
m_foo = getVector();

The getVector may construct a new std::vector, and return it as a temporary object, then the std::vector::operator= will delete the old content of m_foo, then copy all the data from the temporary vector to m_foo. At the end of the statement, the temporary vector will be destroyed, and its destructor will delete its data. It would be way more efficient if instead, the operator= would exchange m_foo data with the temporary vector's data. That way, m_foo's old data would be deleted when the temporary is destroyed, and we would not create a useless copy. This is what the move semantics in C++0x is, and it is achieved through rvalue references.

Even if copying an implicitly shared class is cheap, it does not come for free, we still have to increase and decrease the reference count, and the calls to the operator= cannot be inlined, since it accesses the private data. (We can't inline that to ease binary compatibility).
So now, look at the Qt 4.8's QImage move operator:

#ifdef Q_COMPILER_RVALUE_REFS
    inline QImage &operator=(QImage &&other)
    { qSwap(d, other.d); return *this; }
#endif

We just swap the internal data of the two images, this is a very cheap operation compared to the normal operation which requires a function call. We added this to most of the implicitly shared classes in Qt.
Since the operator= of all our containers is used a lot with temporaries, it may give small improvements to the speed of Qt, which might be a reason to compile Qt with C++0x support (see below).

Range-based for-loop

Qt had already a very convenient foreach, You can also find it in some other library, such as Boost.
The Qt foreach works with complicated macro, But C++0x goes further and make this construct part of the language.
So, instead of writing

foreach(const QString &amp;option, optionList) { ... }

you can write

for (const QString &amp;option : optionList) { ... }

There is a slight difference between the two: Qt does a copy of the container before iterating.
This is cheap for Qt container as they use implicit sharing, but is expensive with standard containers that do a deep copy of the whole content.
The C++0x range-based for does not copy, which means the behaviour is undefined if you add or remove items from the container as you iterate.
The new for will call the begin() and end() functions which are going to make a copy of the Qt container if it is shared and non const. Hence, it is better to pass const containers.

template<class T> const T &const_(const T &t) { return t; }
for(auto it : const_(vector)) { ... }

Lambda

We tested lambda with some of the QtConcurrent functions.
We tested it with QtConcurrent::run and QtConcurrent::map
But it does not work yet for QtConcurrent::mapped, we may fix that in the future.

So the scale example of the Qtconcurrent::map documentation could be rewriten like that

  QList<QImage> images = ...
  QFuture<void> future = QtConcurrent::map(images, [](QImage &image) {
            image = image.scaled(100,100);
        });

I am also researching using lambda in signal/slots connections, but that would be for Qt5.

Unicode Strings

We do not yet support the new string types. But that might come later.

Go and use it!

If you are using MSVC 2010, there is nothing to do, you can already use some feature such as the lambdas or the rvalue references.

If you are using gcc, you need to pass the flag -std=c++0x.
You can do that in your qmake project file like this:

QMAKE_CXXFLAGS += -std=c++0x

If you want to compile Qt using C++0x you can do

CXXFLAGS="-std=c++0x" ./configure

Qt compiled with C++0x is binary compatible with the good old C++. And you do not need to compile Qt with C++0x to write your own application using C++0x,


Blog Topics:

Comments