参考原文:http://woboq.com/blog/cpp11-in-qt5.html
C++ 11 现在已经是 C++ 标准,也就没有理由不在新的应用中使用。Qt 4.8 是第一个支持 C++ 11 特性的 Qt 版本,不过这里,我们首先介绍的是,Qt 5 中如何结合使用 C++ 11。至于 Qt 4.8,我们会在后续文章中进行阐述。
显而易见的是,比起 Qt 4.8,Qt 5 利用了更多的 C++ 11 新特性。下面我们来一个个见识一下:
slot 中使用 Lambda 表达式
Lambda 表达式是 C++ 11 带来的最激动人心的特性之一(豆子已经在前面几篇文章中不止一次说这样的话了 ;-P)。简而言之,它允许创建匿名函数。匿名函数则允许我们直接将一个函数作为参数传递,无需显式地声明。
Qt 4.8 实际已经可以使用这个特性。只不过在 Qt 4.8 中,Lambda 表达式只能用在 QtConcurrent 的某些函数。现在,前面我们也介绍过,Qt 5 有新的 signal/slot 语法,Lambda 表达式有了更大的用武之地。回忆一下,在你需要编写 slot 代码的时候,即使只有一条语句,你也必须为它单独建立一个函数。这不是很麻烦吗?现在,我们有了更好的写法:
connect(sender, &Sender::valueChanged, [=](const QString &newValue) { receiver->updateValue("senderValue", newValue); });
Lambda 表达式现在已经被 MSVC 2010、GCC 4.5 和 clang 3.1 实现。
Unicode 字符串常量
C++ 11 允许你使用 u"HelloWorld" 的形式生成 UTF-16 字符串。Qt 利用这个特性增加了一个新的类QStringLiteral
。这个类能够在编译时初始化QString
,没有了运行时的时间消耗。
QString str = QStringLiteral("HelloWorld");
常量表达式 constexpr
C++ 11 增加了新的关键字constexpr
,指示某些 inline 函数可以在编译期运算。在 Qt 5 中,我们引入了Q_DECL_CONSTEXPR
宏,当所使用的编译期支持constexpr
时,这个宏可以生成constexpr
,否则的话则是空白。
在 Qt 源代码中,我们也利用这个宏改写了许多函数,例如:
enum SomeEnum { Value1, Value2, Value3 }; Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags<SomeEnum>) // 上面一句声明了下述函数: // Q_DECL_CONSTEXPR QFlags<SomeValue> operator|(SomeValue,SomeValue) {...} int someFunction(QFlags<SomeEnum> value) { switch (value) { case SomeEnum::Value1: return 1; case SomeEnum::Value2: return 2; case SomeEnum::Value1 | SomeEnum::Value3: // 这一个 case 仅在 C++ 11 中通过编译 // 因为 QFlags 运算符是 constexpr 的,也就是在编译期即可确定 // 而在之前版本则必须是 // QFlags<SomeValue> operator|(SomeValue,SomeValue) // 这会引发一个错误,因为 case 语句要求编译期常量 return 3; default: return 0; } }
注意,这里我们在枚举值前面使用可SomeEnum::
前缀,这是 C++ 11 允许的,但是之前版本的 C++ 则不允许。
static_assert
在编译期使用static_assert
检测问题,可以让 C++ 11 帮助我们可以组织处更好的错误信息。Qt 5 引入了Q_STATIC_ASSERT
和Q_STATIC_ASSERT_X
两个宏。当static_assert
可用时,这两个宏将使用static_assert
,否则使用一些模板技巧。
为了产生更好的编译错误信息,Qt 在 API 不方便的地方大量使用了宏。
override
和final
你遇到过这样的错误吗?自己定义的函数名看上去同父类的某个函数同名,但却的确有某些字母打错了,以至于并没有覆盖父类函数,从而让程序不能正确运行(或者是忘记了那函数名最后面的该死的const
)?
现在,你可以选择在的确需要覆盖父类虚函数的地方加上Q_DECL_OVERRIDE
。如果编译器支持的话,这个宏将展开为新增加的“override
”关键字。这样的话,如果编译器支持 C++ 11,那么如果是简单的字母错误,你就会得到一个错误;当你重构虚函数、却忘记修改子类时,同样会引发一个错误。
class MyModel : public QStringListModel { //... protected: Qt::ItemFlags flags (const QModelIndex & index) Q_DECL_OVERRIDE; };
注意,上面的flags()
函数实际是想覆盖父类的同名函数,但是我们忘记了一个const
,就会出现类似下面的错误:
mymodel.h:15: error: `Qt::ItemFlags MyModel::flags(const QModelIndex&)` marked override, but does not override
如果虚函数不能覆盖,Qt 也提供了另外一个宏,Q_DECL_FINAL
,这个宏展开为final
。
deleted 成员
当编译器支持 deleted 函数时,新增加的宏Q_DECL_DELETE
将展开为=delete
。这就允许我们能够为一些常见错误提供更好的编译器错误信息。
deleted 函数用于显式地删除那些不允许编译器自动生成的函数(例如默认构造函数、默认拷贝运算符等)。deleted 函数不能被调用,如果被使用的话,将会出现一个编译器错误。
我们可以将其用于Q_DISABLE_COPY
宏。在此之前,为了实现同样的目的,我们的做法是将其声明为private
的。尽管效果相同,但是错误信息却并不友好。
右值引用和移动构造函数
在这里,我假定你明白什么叫做“右值引用”。如果不明白,我们会在后面的文章中详细说明。Qt 5 已经在内部进行了调整,以便支持移动构造函数。因此,你可以大胆的使用他们了!
结论
对于 C++ 11,MSVC 不需要任何特殊的编译参数,而 GCC 和 Clang 则需要添加 -std=c++0x。
默认情况下,Qt 5 本身会使用 C++ 11 编译参数进行编译(如果可能的话)。
如果你使用的是 qmake,那么,在使用 Qt 5 构建的程序的 .pro 文件中,你需要增加这么一句:
CONFIG += c++11
(顺便提一句,在 Qt 4 中,如果你需要使用 C++ 11 的新特性,则应该增加gcc:CXXFLAGS += -std=c++0x
。具体细节,我们会在后面的文章中说明。)
现在,好好利用 C++ 11 所带来的新特性吧!私以为,仅仅为了auto
这一特性,就应该尽快使用 C++ 11 了!;-p
1 个评论
期待C++11普及