面向 Qt 程序员的 C++14

原文链接:https://woboq.com/blog/cpp14-in-qt.html 作者:Olivier Goffart 发表于:2014 年 11 月 17 日


C++14 是当年即将发布的新一代 C++ 标准。相比于引入大量新特性、并且花费了编译器厂商很长时间才完全支持的 C++11,C++14 的改动要轻量得多,目前已被 clang、gcc 等主流编译器良好支持。

Qt 5 在多个方面已经完成了对 C++11 的适配,使开发者能够在 Qt 项目中使用现代 C++ 特性。关于这部分内容,可以参考我之前的文章《Qt5 中的 C++11》。本文将重点介绍 C++14 中的一些变化,以及它们对 Qt 用户的影响。


泛型 Lambda(Generic Lambda)

C++11 引入了 Lambda 表达式,而 Qt 5 则允许你使用新的 connect 语法,将信号直接连接到 Lambda。 C++14 进一步简化了 Lambda 的使用方式:参数类型可以自动推导,无需显式指定。

你可以使用 auto 作为 Lambda 参数类型,例如:

从实现角度来看,Lambda 本质上是一个带有 operator() 的仿函数对象。 对于泛型 Lambda,这个 operator() 会被生成为一个模板函数。

我曾为此对 Qt 做过一次修改,该改动已在 Qt 5.1 中合入,使 Qt 的 connect 能够正确支持这类仿函数。

C++14 还允许在捕获列表中使用表达式,这让代码更加清晰:


放宽的常量表达式(Relaxed Constant Expressions)

C++11 引入了关键字 constexpr。Qt 4.8 增加了宏 Q_DECL_CONSTEXPR,在编译器支持时会展开为 constexpr。在 Qt 5 中,我们已经尽可能多地在合适的函数上使用了它。

C++14 对 constexpr 的限制进行了大幅放宽:

而在 C++14 中,只要代码可以在编译期求值,几乎所有形式的实现都是允许的。

例如,下面的代码在 C++11 中是非法的,但在 C++14 中完全合法:

另外需要注意一个重要变化:

这意味着: 如果某个成员函数在 C++11 中被声明为 constexpr,但未显式标注 const,那么在 C++14 中它的 const 属性将发生变化,从而可能导致二进制不兼容

好消息是:在 Qt 中,所有使用 Q_DECL_CONSTEXPR 的成员函数都明确声明了 const,因此保持了 ABI 兼容性。

从 Qt 5.5 开始,引入了一个新的宏:

当使用 C++14 编译时,它会展开为 constexpr,从而允许我们逐步为更多合适的函数(例如 operator=)添加该标注。


一些实用的小特性

C++14 还引入了不少提升可读性和开发体验的小特性。 这些特性对 Qt 本身影响不大,但只要启用了 C++14,你就可以在自己的代码中放心使用。 我们也确保了诸如 moc 这样的 Qt 工具能够正确解析它们。

数字分组分隔符

可以在数值字面量中使用 ' 作为分隔符,提升可读性:


二进制字面量

现在可以使用 0b 前缀表示二进制数:


自动返回类型推导

对于内联函数,可以使用 auto 作为返回类型,让编译器自动推导:

⚠️ 注意: 这种写法不能用于 Qt 的 slot 或 Q_INVOKABLE 方法,因为 moc 无法推断返回类型。


变量模板(Variable Template)

C++14 允许定义变量模板,而不仅仅是函数或类模板:


支持默认成员初始化的聚合初始化

C++11 中,聚合初始化与非静态成员初始化不能同时使用。 C++14 消除了这一限制,使以下代码变得合法且直观:


引用限定符(Reference Qualifiers)

虽然这是 C++11 的特性,但 Qt 在 5.x 生命周期后期才开始大量使用,因此在此简单介绍。

考虑以下代码:

fromUtf8() 返回的是一个临时对象。如果 toLower() 能够复用已有内存并原地转换,将显著提升性能。 引用限定符正是为此而生。

简化后的实现示意:

&&& 表示根据 this 的值类别(左值 / 右值)选择不同实现。

toLower() 作用于临时对象时,会调用 && 版本,从而避免不必要的拷贝。

在 Qt 5.4 中,以下函数已受益于该优化:


标准库的变化

C++11 与 C++14 为标准库带来了大量新特性,其中不少功能与 QtCore 存在重叠。

然而,Qt 对标准库的使用依然非常克制,主要原因包括:

不过,Qt 5 已弃用自身提供的算法库,并明确推荐使用 STL 算法,例如:


结论

在项目中全面采用这些特性可能仍需时间,但我希望你已经像 Qt Creator、KDE、LLVM 等项目一样,开始使用 C++11,并逐步过渡到 C++14。

目前:

在 Qt 5.4 及之后的版本中,只需在 .pro 文件中加入:

即可启用 C++14 支持。