Saludos amigos, ésta es la segunda de tres entregas acerca del uso del nuevo estándar de C++ en Qt en este caso de Qt5, una vez más agradecer a Olivier Goffart por permitir su traducción para humanOS. Los que deseen pueden encontrar el artículo en su blog oficial en el idioma inglés.

C++11 en Qt5

Publicado el 11 de junio de 2012 por Olivier Goffart

C++11 es el nombre de la versión actual del estándar de C++ el cual aporta muchas características al lenguaje.

Qt 4.8 fue la primera versión de Qt que comenzó a usar algunas de las características del nuevo C++1 en su API. Escribí un artículo acerca de C++11 en Qt 4.8 (ES) antes de la salida de 4.8 el cual no voy a repetir aquí.

En Qt5 hacemos uso incluso de más características. Entraré en detalle acerca de algunas en este post.

Expresiones Lambda para slots

Las expresiones Lambda son una nueva sintaxis en C++11 que permite declarar funciones anónimas. Las funciones anónimas pueden ser utilizadas con el fin de usar pequeñas funciones como parámetros sin requerir de su declaración explícita. La forma anterior de crear functors usando operator() en una estructura requería de bastante código repetitivo.

Ya era posible en Qt 4.8 usar expresiones lambda en algunas funciones de QtConcurrent. Ahora incluso es posible usarlas como slots usando la nueva sintaxis de connect.

Recuerde cuando tenía que escribir una función de una sola línea para su slot. Ahora puede escribirla justamente en el lugar. Es mucho más entendible tener una función directamente donde se referencia:

connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
    receiver->updateValue("senderValue", newValue); });

Las funciones lambda son permitidas a partir de MSVC 2010, GCC 4.5 y clang 3.1.

Literales Unicode

En C++11 se puede generar UTF-16 escribiendo u"MyString". Esto es usado por Qt para implementar QStringLiteral la cual es una macro que inicializa un QString en tiempo de compilación sin afectar el rendimiento en tiempo de ejecución.

QString someString = QStringLiteral("Hello");

Ver mi artículo anterior acerca de QStringLiteral.

Expresiones constantes: constexpr

Esta nueva palabra clave en C++11 constexpr puede ser añadida para señalar que alguna función inline puede ser procesada en tiempo de compilación. En Qt 5, introducimos Q_DECL_CONSTEXPR la cual es definida como constexpr solo si el compilador lo soporta.

También anotamos algunas de las funciones de Qt para las cuales tenía sentido (por ejemplo QFlags), lo cual les permite ser usadas en expresiones constantes.

enum SomeEnum { Value1, Value2, Value3 };
Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags)
// The previous line declares
// Q_DECL_CONSTEXPR QFlags operator|(SomeValue,SomeValue) {...}
 
int someFunction(QFlags value) {
    switch (value) {
        case SomeEnum::Value1:
            return 1;
        case SomeEnum::Value2:
            return 2;
        case SomeEnum::Value1 | SomeEnum::Value3:
        // Only possible with C++11 and because QFlags operators are constexpr
        // Previously this line would call
        //        QFlags operator|(SomeValue,SomeValue)
        // that would have thrown an error because only compiler constants
        // are allowed as case satement
 
            return 3;
        default:
            return 0;
    }
}

(Note aquí que uso SomeEnum:: en frente de los valores lo cual es permitido en C++11 pero no lo era anteriormente)

static_assert

C++11 ayuda a la hora de producir mejores mensajes de error cuando hay algo que sale mal y puede ser detectado en tiempo de compilación usando static_assert. En Qt5 introducimos las macros Q_STATIC_ASSERT y Q_STATIC_ASSERT_X que usan static_assert solo si están disponibles o algún otro truco con templates si no lo está.

Qt ya utiliza esta macro intensivamente con el objetivo de proveer mejores errores de compilación cunado la API no es usada adecuadamente.

Override y final

¿Alguna vez ha tenido un código que no funciona porque cometió algún error con el nombre de una función virtual que está re implementando? (O olvidó el maldito const al final)

Ahora puede señalar las funciones que se supone que re definirán a otras funciones virtuales con Q_DECL_OVERRIDE.

Esta macro será substituida con el nuevo atributo “override” solo si el compilador lo soporta. Si compila su código con un compilador con C++11 activado tendrá un error de compilación en caso de que exista un error en el nombre de la función o si ha cambiado un método virtual mientras refactorizaba.

class MyModel : public QStringListModel {
    //...
protected:
     Qt::ItemFlags flags (const QModelIndex & index)  Q_DECL_OVERRIDE;
};

Debido a que olvidamos el const esto producirá un error así:

mymodel.h:15: error: `Qt::ItemFlags MyModel::flags(const QModelIndex&)`
 marked override, but does not override

Existe también un Q_DECL_FINAL el cual es substituido por el atributo final el cual especifica que una función virtual no puede ser re definida.

Miembro eliminado

La nueva macro Q_DECL_DELETE se expande a = delete para compiladores que permiten funciones eliminadas. Esto es útil para obtener mejores errores del compilador para evitar equivocaciones comunes.

Las funciones eliminadas son usadas para eliminar explícitamente funciones que serían creadas automáticamente por el compilador (tales como constructores por defecto y el operador de copia por asignación). Las funciones eliminadas no pueden ser llamadas porque se lanzaría un error del compilador.

Lo usamos por ejemplo en la macro Q_DISABLE_COPY. Antes el truco era mantener esos miembros privados pero no producía un mensaje de error tan correcto.

Referencias Rvalue y Constructores Move

En el artículo de Qt 4.8 (ES) ya había explicado de que tratan las referencias rvalue.

Debido a un cambio en el interior de Qt acerca del conteo de referencia de nuestras clases compartidas en Qt5, fue posible añadir un constructor move para muchas de ellas.

Conclusión

MSVC no requiere algún flag especial para habilitar las características de C++11 por defecto pero GCC o Clang requieren de –std=c++0x.

Por defecto, Qt5 por sí mismo va a ser compilado con el flag C++11 en compiladores que lo necesiten.

Si usas qmake, puedes añadir una línea a tu fichero .pro (Qt5):

CONFIG += c++11

(En Qt4, debería ser algo como: gcc:CXXFLAGS += -std=c++0x)

Y ahora puedes disfrutar de todas las agradables características de C++11. (ya vale la pena solamente con poder usar auto)