JEP 347:启用 C++14 语言特性
总结
允许在 JDK C++ 源代码中使用 C++14 语言特性,并针对其中哪些特性可以在 HotSpot 代码中使用给出具体指导。
目标
在 JDK 15 之前,JDK 中的 C++ 代码所使用的语言特性一直局限于 C++98/03 语言标准。从 JDK 11 开始,代码已经更新为支持使用较新版本的 C++ 标准进行构建,尽管尚未使用任何新特性。这包括能够使用支持 C++11/14 语言特性的各种编译器的最新版本进行构建。
此 JEP 的目的是正式允许在 JDK 内部进行 C++ 源代码更改,以利用 C++14 语言特性,并就 HotSpot 代码中可以使用哪些特性提供具体指导。
非目标
该 JEP 并未提议对 JDK 中 HotSpot 之外的 C++ 代码进行任何使用或风格上的更改。HotSpot 和非 HotSpot 代码的规则已经有所不同。例如,某些非 HotSpot 代码中使用了 C++ 异常,但通过构建时选项禁止在 HotSpot 中使用异常。然而,构建一致性要求将使更新的语言特性可用于 JDK 中的所有 C++ 代码。
描述
构建系统的更改
为了利用 C++14 语言特性,需要进行一些构建时的更改,具体取决于平台编译器。还需要指定各个平台编译器的最低可接受版本。应该明确指定所需的语言标准;较新的编译器版本可能会默认使用更新的、可能不兼容的语言标准。
-
Windows: JDK 11 需要 Visual Studio 2017。(早期版本会生成配置时警告,可能可以工作,也可能不行。)对于 Visual Studio 2017,默认的 C++ 标准是 C++14,应添加
/std:c++14
选项。对旧版本的支持将完全取消。 -
Linux: 将编译器选项
-std=gnu++98
替换为-std=c++14
。支持的 gcc 最低版本为 5.0。 -
macOS: 将编译器选项
-std=gnu++98
替换为-std=c++14
。支持的 clang 最低版本为 3.5。 -
AIX/PowerPC: 将编译器选项
-std=gnu++98
替换为-std=c++14
,并要求使用 xlclang++ 作为编译器。支持的 xlclang++ 最低版本为 16.1。
HotSpot 代码中 C++ 使用的变更
HotSpot 代码中现有的 C++ 使用限制和最佳实践建议基于 C++98/03 语言标准,并在 HotSpot 风格指南 中有所描述。
我们将在该文档中添加针对更近期语言标准特性的类似限制和指南。这些内容将通过一张允许使用的特性表和另一张排除使用的特性表来描述。允许使用的特性可能无条件使用,或者可能有一些限制或额外的指导。在 HotSpot 代码中,禁止使用被排除的特性。
还有第三类,未确定功能,HotSpot 开发者们对此尚未达成共识,甚至可能根本没有讨论过。使用这些功能也是被禁止的。
但是,请注意,某些语言特性的使用可能不会立即显现出来,并且可能会无意中混入代码中,因为编译器会接受它们。一如既往,代码审查过程是防止这种情况的主要手段。
对某个特性分类的拟议变更需要由 HotSpot 组的成员通过粗略共识来批准,具体由组长决定。此类变更必须在《风格指南》的更新中加以记录。
C++11 和 C++14 的新特性列表以及它们的描述链接可以在一些编译器和库的在线文档中找到:
根据经验,允许简化编写代码的特性,尤其是简化阅读代码的特性,是受到鼓励的。
HotSpot 在很大程度上避免使用 C++ 标准库。这样做的某些原因可能已经过时(尤其是早期版本中遇到的 bug),而其他原因可能仍然适用(尽量减少依赖)。对标准库各部分的分类应当经历与语言特性相同的过程。
以下是 HotSpot 的初始功能分类集。
允许
-
constexpr
放宽的
constexpr
(n3652) 可能是区分 C++14 和 C++11 的关键特性。constexpr
特性将允许消除一些笨拙的宏代码,转而使用constexpr
函数。此特性也是简化元编程习惯用法的基础。参见 mpl11 和 mpl11_2。 -
定长释放 (n3778) — 由于 Visual Studio 的原因,该语法已经在使用中。
-
可变参数模板
-
静态断言 (n1720) — 替代 HotSpot STATIC_ASSERT 宏,提供更好的错误消息。
-
decltype
重要的元编程工具。需要实现简化的函数模板 SFINAE 工具。
-
尖括号 (n1757) — 消除了一个令人讨厌的语法疣。
-
函数模板的默认模板参数 (CWG D226) — 除了本身有用外,还需要实现简化的函数模板 SFINAE 工具。
-
模板别名 (n2258) — 带有模板参数的 Typedef。提供了类模板的部分特化语法,而不是使用继承(有时不适当)。在简化的元编程方法中也用作元函数 mpl11 和 mpl11_2。
-
强类型枚举 (n2347) — 允许显式控制枚举的底层类型,而不是让其可能依赖于实现(并在不同实现之间变化)。还允许对枚举类进行强类型化,消除隐式转换。建议当枚举器确实是一组逻辑值时使用 scoped-enums。允许使用 unscoped-enums,不过在未使用自动初始化功能时应优先选择普通常量。应始终明确指定 enum-base,而不是让它依赖于枚举器值的范围和平台。
-
委托构造函数 (n1986) — 通过允许专用构造函数链式调用更通用的构造函数来消除一些代码重复并简化。
-
显式转换运算符 (n2437) — 用于使某些现有的转换运算符安全。
-
标准布局类型 (n2342)
-
默认和删除的函数 (n2346)
-
使用并发的动态初始化和销毁 (n2660) — 线程安全的函数局部静态变量。
-
<type_traits>
是核心元编程库。它消除了许多 HotSpot 元编程工具的需求,这些工具模仿了该库的相应部分。 -
类和虚函数的
final
虚拟说明符 (n2928), (n3206), (n3272) —final
说明符允许虚函数调用的去虚拟化。这可以比依赖编译器使用诸如指针分析或推测性去虚拟化等技术提供更好的性能。参考文献中描述的虚函数的overrides
说明符可以在稍后考虑。 -
局部和匿名类型作为模板参数 (n2657) — 允许局部定义仅使用一次的帮助类放置在使用点附近,当使用是模板时,而不必要求此类帮助程序在命名空间范围内定义。
-
nullptr
和std::nullptr_t
(n2431) -
auto
变量声明 (n1984) — 仅在使代码更清晰或更安全时使用。不要仅仅为了避免编写显式类型的不便而使用它,除非该类型本身难以编写。对于局部变量,可以通过消除明显或无关的类型信息来使代码更清晰。过度使用会使代码更难理解。 -
函数返回类型推导 (n3638) — 仅当函数体中有非常少的
return
语句,并且通常相对较少的其他代码时使用。 -
表达式 SFINAE (n2634)
排除
-
新的字符串和字符字面量
HotSpot 不需要任何新的字符和字符串字面量类型。
-
用户定义的字面量 (n2765) — 用户定义的字面量不应随意添加,而应通过提案来添加特定的 UDL。
-
内联命名空间 (n2535) — HotSpot 对命名空间的使用非常有限。
-
using namespace
指令。特别是,不要使用using namespace std;
来避免需要限定标准库名称。 -
异常传播 (n2179) — HotSpot 不允许使用异常,因此此功能没有用处。
-
thread_local
(n2659) — 使用 HotSpot 的THREAD_LOCAL
宏;有关讨论请参见 JDK-8230877。 -
[[deprecated]]
属性 (n3760) — 在 HotSpot 代码中不相关。
其他一些项目的类似列表
-
Google C++ 风格指南 — 当前目标是 C++11,不应使用 C++14。
-
C++11 和 C++14 在 Chromium 中的使用 — 将特性分类为允许、禁止或待讨论。
-
llvm 编码标准 — 当前目标是 C++11,不应使用 C++14。
-
在 Mozilla 代码中使用 C++ — 需要支持 C++14。
风险与假设
可能还有其他平台的工具链尚不支持 C++14 语言标准。
某些编译器对一些新特性的支持可能存在 bug。