原文:Olivier Goffart 发布时间:2012 年 6 月 11 日 原文链接:https://woboq.com/blog/cpp11-in-qt5.html
C++11 是当时最新的 C++ 标准版本,为语言本身引入了大量重要的新特性。
Qt 4.8 是第一个在其 API 中开始使用部分 C++11 特性的 Qt 版本。
在 Qt 4.8 发布之前,我曾写过一篇关于 Qt 4.8 中 C++11 的博客文章,本文将不再重复那些内容。
在 Qt 5 中,我们进一步扩大了对 C++11 的使用范围。下面将详细介绍 Qt 5 中采用的一些关键 C++11 特性。
Lambda 表达式是 C++11 引入的一种新语法,用于定义匿名函数。
当需要将一个简短的函数作为参数传递时,Lambda 表达式可以避免为此单独声明一个函数,从而显著减少样板代码。
在 C++11 之前,通常需要通过重载 operator() 的结构体来实现仿函数,这种方式既冗长又影响可读性。
在 Qt 4.8 中,Lambda 已经可以用于部分 QtConcurrent 相关的函数;
而在 Qt 5 中,借助 新的 connect 语法,我们甚至可以直接将 Lambda 表达式用作槽函数。
还记得过去为了一个只有一行代码的槽函数,不得不单独写一个成员函数吗?
现在可以直接在使用的位置定义逻辑,使代码更加直观、易读:
xxxxxxxxxx41connect(sender, &Sender::valueChanged,2[=](const QString &newValue) {3receiver->updateValue("senderValue", newValue);4});
支持 Lambda 表达式的编译器包括:
MSVC 2010
GCC 4.5
Clang 3.1
在 C++11 中,可以通过 u"MyString" 的形式直接生成 UTF-16 字符串字面量。
Qt 正是基于这一特性实现了 QStringLiteral。
QStringLiteral 是一个宏,用于在编译期初始化 QString,从而避免运行时构造带来的开销。
xxxxxxxxxx11QString someString = QStringLiteral("Hello");
关于 QStringLiteral 的更多细节,可以参考我之前的相关博客文章。
constexprC++11 引入了 constexpr 关键字,用于标记可在编译期求值的函数或表达式。
在 Qt 5 中,我们引入了 Q_DECL_CONSTEXPR 宏:
当编译器支持 constexpr 时,该宏展开为 constexpr
否则为空
Qt 已经为一些合适的 API(例如 QFlags)添加了该标注,使它们能够用于常量表达式。
示例:
xxxxxxxxxx191enum SomeEnum { Value1, Value2, Value3 };2Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags<SomeEnum>)3// 上一行会声明:4// Q_DECL_CONSTEXPR QFlags<SomeEnum> operator|(SomeEnum, SomeEnum) { ... }56int someFunction(QFlags<SomeEnum> value)7{8switch (value) {9case SomeEnum::Value1:10return 1;11case SomeEnum::Value2:12return 2;13case SomeEnum::Value1 | SomeEnum::Value3:14// 只有在 C++11 且 QFlags 的运算符是 constexpr 时才合法15return 3;16default:17return 0;18}19}
注意:
这里使用了 SomeEnum::Value1 这种作用域限定写法,这在 C++11 中是允许的,而在旧标准中并不支持。
static_assertC++11 提供了 static_assert,用于在编译期检测错误,并给出更清晰的错误信息。
Qt 5 中引入了以下宏:
Q_STATIC_ASSERT
Q_STATIC_ASSERT_X
当编译器支持 static_assert 时,这些宏会直接使用该特性;否则会退化为模板技巧实现。
Qt 在内部大量使用这些宏,以便在 API 被误用时,能提供更友好的编译期错误提示。
override 与 final你是否曾因为以下问题而浪费时间调试?
重写虚函数时函数名拼写错误
忘记在函数末尾加上 const
函数签名不完全匹配,却没有任何编译错误提示
在 Qt 5 中,可以使用 Q_DECL_OVERRIDE 来标注重写的虚函数:
xxxxxxxxxx41class MyModel : public QStringListModel {2protected:3Qt::ItemFlags flags(const QModelIndex &index) Q_DECL_OVERRIDE;4};
当编译器支持 C++11 时,Q_DECL_OVERRIDE 会展开为 override;否则为空。
如果函数签名不匹配,编译器会直接报错,例如:
xxxxxxxxxx21error: 'Qt::ItemFlags MyModel::flags(const QModelIndex&)'2marked override, but does not override
此外,Qt 还提供了 Q_DECL_FINAL,用于标记某个虚函数不能再被重写。
= delete)Qt 引入了 Q_DECL_DELETE 宏,在支持的编译器上会展开为 = delete,用于显式删除函数。
这一特性常用于禁止以下行为:
默认构造
拷贝构造
拷贝赋值
Qt 在 Q_DISABLE_COPY 宏中使用了该机制。
相比旧版本中通过将函数声明为 private 的方式,= delete 能提供更直观、清晰的编译错误信息。
在之前关于 Qt 4.8 的文章中,我已经介绍过右值引用的基本概念。
在 Qt 5 中,由于共享类内部引用计数实现的调整,我们得以为许多 Qt 类型添加了 移动构造函数,从而显著提升性能,减少不必要的拷贝。
MSVC 默认启用了 C++11 特性,无需额外编译选项
GCC / Clang 需要显式指定 -std=c++0x(当时)
Qt 5 会在支持的编译器上自动启用 C++11
如果你使用 qmake,可以在 .pro 文件中添加:
xxxxxxxxxx11CONFIG += c++11
在 Qt 4 中通常需要写成:gcc:CXXFLAGS += -std=c++0x
现在,你可以尽情享受 C++11 带来的各种优秀特性了——
哪怕只是为了能用 auto,也已经非常值得升级了。