Saludos humanos, les traigo el primero de tres artículos publicados por Oliver Goffart (gracias por permitirlo) acerca del uso de Qt con los nuevos estándares de C++.

Algunos encontrarán este primer Post un poco desactualizado ya que se publicó en mayo de 2011 pero les aseguro que su contenido se encuentra vigente. Se abordará acerca de los aportes de C++11 o C++0x que influyen en Qt 4.8, para los que deseen leerlo directamente pueden encontrarlo en el blog de Qt.

 C++0x en Qt

Publicado el jueves 26 de mayo de 2011 por Oliver Goffart.

Mientras que muchos son entusiastas de QML y la tecnología JavaScript, unos pocos de nosotros todavía programamos en C++ 😉 . C++ está cerca de tener una actualización: C++11 (formalmente conocido como C++0x). El borrador final fue aprobado el pasado marzo en el comité del estándar C++, y se espera que la publicación final de la especificación sea este verano. Si no la conoces todavía, te invito a leer las páginas especializadas, tales como Wikipedia o el FAQ de C++0x.

Una de las metas supuestamente de C++0x es hacer el lenguaje más fácil. Lo cual es un poco difícil porque solamente agrega más cosas a aprender, pero con el deseo de que el sub conjunto “más usado” de C++ sea más fácil y más intuitivo.

La buena noticia es que no tienes que esperar para usarlo. Puedes empezar hoy mismo. El compilador que usas posiblemente ya tenga soporte para C++0x: GCC o MSVC 2010.

Mientras que puedes usar C++0x en versiones anteriores de Qt como Qt 4.7, Qt 4.8 viene con un mayor soporte para la nueva estructura de C++0x:

Nuevas Macros

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

Inicializadores de listas

Qt 4.8 le adiciona un nuevo constructor a QVector, QList y QStringList el cual permite que se los inicialice con el inicializador de llaves.

Puedes hacer:

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

lo que inicializa los contenedores con esos elementos.

Referencias Rvalue y semánticas move

La mayoría de las clases de Qt son implícitamente compartidas, lo que se traduce en eficiencia a la hora de copiarlas si no las modificamos (copia al escribirse). Esto no es el caso de std::vector, donde cada copia implica la copia de todos los datos.

Así que si tenemos un código como:

std::vector m_foo;
...
m_foo = getVector();

El getVector construye un nuevo std::vector, y lo retorna como un objeto temporal, luego el std::vector::operator= eliminará el contenido antiguo de m_foo, luego copia todos los datos del vector temporal al vector m_foo. Al final de la sentencia, el vector temporal será destruido y su destructor eliminará todos sus datos. Sería más eficiente si el operator= intercambiara los datos de m_foo con los datos del vector temporal. Así los datos antiguos de m_foo serían eliminados cuando el objeto temporal sea destruido y no creamos una copia innecesariamente. Esto es a lo que se refiere una semántica move en C++0x y es logrado por medio de referencias rvalue.

Considerando que si copiamos una clase implícitamente compartida es poco costoso, esto no viene gratis, todavía debemos incrementar y decrementar el contador de referencia y la llamada al operator= no puede ser inline dado a que este accede a los datos privados. (No podemos hacerle inline para facilitar la compatibilidad binaria).

Ahora veamos el operador move de QImage en Qt 4.8.

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

Solamente intercambiamos los datos internos de las dos imágenes, esto es poco costoso comparado con la operación normal que requiere de una llamada a función. Hemos añadido esto a la mayoría de las clases implícitamente compartidas de Qt. Dado a que operator= de todos los contenedores es usado bastante con temporales, esto puede traer una pequeña mejora en la velocidad de Qt, lo cual puede ser una razón para querer compilar Qt con soporte para C++0x (ver abajo).

Ciclos for basados en rango

Qt ya tiene un foreach muy conveniente, lo podemos encontrar en otras bibliotecas como la Boost. El foreach de Qt trabaja con una macro complicada pero C++0x va más allá y lo hace parte del propio lenguaje.

Así que en vez de escribir:

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

podemos escribir:

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

Hay una ligera diferencia entre los dos: Qt hace una copia del contenedor antes de iterar. Esto es eficiente para contenedores de Qt que son implícitamente compartidos pero es costoso para contenedores estándar que hacen una copia en profundidad de todo el contenido.

El for basado en rango de C++0x no hace una copia, lo que significa que el comportamiento es indefinido si añadimos y eliminamos elementos al contenedor mientras iteramos.

El nuevo for llama las funciones begin() y end() las cuales harán una copia del contenedor Qt si es compartido y no const, por lo que es mejor pasar contenedores como const.

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

Lambda

Hemos probado lambda con algunas de las funciones de QtConcurrent.

Las hemos probado con QtConcurrent::run y QtConcurrent::map pero todavía no funciona para QtConcurrent::mapped, debemos arreglarlo en un futuro.

Asi que si escalamos el ejemplo de la documentación de QtConcurrent, puede ser re escrito como:

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

Estoy también investigando como usar lambda en conexiones signal/slot, pero eso será para Qt5.

Cadenas Unicode

Todavia no soportamos los nuevos tipos de cadenas pero puede venir después,

Anímate y úsalo!

Si estás usando MSVC 2010, no hay nada que hacer, ya puedes usar algunas características como las lambdas y las referencias rvalue.

Si estás usando gcc, necesitas poner el flag –std=c++0x

Puedes hacerlo en tu proyecto de qmake asi:

QMAKE_CXXFLAGS += -std=c++0x

Si deseas compilar Qt usando C++0x puedes hacer:

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

El Qt compilado con C++0x es binariamente compatible con el buen antiguo C++ y no necesitas compilar Qt con C++0x para escribir tus propias aplicaciones usando C++0x.