了解更改默认 C++ 模板编译模型的影响

Oracle Solaris Studio 12.4

作者:Steve Clamage

了解更改 Oracle Solaris Studio 默认模板编译模型对代码的影响。


2014 年 9 月发布


想对本文发表评论吗?请将链接发布在 Facebook 的 OTN Garage 页面上。有类似文章要分享?请将其发布在 Facebook 或 Twitter 上,我们来进行讨论。

在 Oracle Solaris Studio 12.4 中,默认模板编译模型已经由 Oracle Solaris Studio 12.3 及早期版本中使用的 definitions-separate 模型更改为 definitions-included 模型。大多数应用将受益于这种变化。有些应用要恢复到早期版本的行为,则需要在 Oracle Solaris Studio C++ 编译器 (CC) 命令行中添加显式选项 -template=extdef。本文介绍此变化及其可能对构建 C++ 应用产生的影响。

背景

Oracle Solaris Studio 12.4:C++ 用户指南 第 5 章讨论了组织模板源代码的两种方式:

  • Definitions-separate。头文件提供一个或多个模板的声明(接口),定义则包含在一个单独的 .cc 文件中。在需要的地方包含该 .cc 文件。对应的 C++ 编译器选项是 -template=extdef
  • Definitions-included。模板定义随模板声明一起提供。对应的 C++ 编译器选项是 -template=no%extdef

过去,Oracle Solaris Studio C++ 编译器沿袭了第一个 C++ 编译器 Cfront 中引入的模板编译模型。因此,默认情况下,编译器使用 definitions-separate 模型。如果头文件 foo.h 包含模板声明,对于任何需要模板定义的编译,编译器自动包括 foo.cc

:本文使用 .cc 作为 C++ 源文件扩展名。本讨论同样适用于 Oracle Solaris Studio C++ 编译器识别的其他扩展名:.c.cpp.C.c++.cxx

Definitions-Separate 模型和源代码组织

definitions-separate 模型对源代码组织有以下要求。

  • 假定 foo.h 中缺失的任何模板定义均在 foo.cc 中。
  • foo.cc 文件只包含 foo.h 中缺失的模板定义。
  • foo.cc 文件不得包含 foo.h
  • foo.cc 文件不得单独编译。

如果您想想只要包含 foo.h 的地方通常都会自动包含 foo.cc,就会立即明白以上限制的原因。

源代码组织示例

清单 1 显示函数模板 times2 的 definitions-separate 组织。

File times2.h
template<class T> T times2(T val);
File times2.cc
template<class T> T times2(T val) { return val*2; }

清单 1. 模板 times2,definitions-separate。

在清单 1 中,根据需要自动包括 times2.cc 文件。

清单 2 显示模板 times2 的 definitions-included 组织。

File times2.h
template<class T> T times2(T val) { return val*2; }

清单 2. 模板 times2,definitions-included。

在清单 2 中,模板定义包含在头文件中。没有单独的 .cc 文件。

清单 3 显示 definitions-included 模型的替代组织。

File times2.h
template<class T> T times2(T val);
#include "times2.cc"
File times2.cc
template<class T> T times2(T val) { return val * 2; }

清单 3. 模板 times2,替代 definitions-included。

在清单 3 中,模板定义在单独的文件中,但该文件通过 #include 指令显式包括。

Oracle Solaris Studio 12.4 中对模板定义模型的更改

在采用 definitions-separate 模板模型的编译器方面,早期版本的 Oracle Solaris Studio 可谓别具一格。原来使用其他编译器开发的代码经常与该模型发生冲突,经常在启用 -template=extdef 选项时不能编译。有些流行的开源代码(尤其是最近版本的 Boost)不能编译。开发人员经常需要在 CC 命令行添加 -template=no%extdef,才能让代码成功编译。

由于这些原因,Oracle Solaris Studio 团队更改了 Oracle Solaris Studio 12.4 中的默认行为,这样 definitions-separate 不再是默认模型。-template 选项的默认值由 -template=extdef 更改为 -template=no%extdef

使用错误模板模型编译的后果

如果实际的源代码模型(包含定义或单独定义)与编译器期待的模型不匹配,会出现什么状况?有两种可能:

  • 如果 -template=extdef 选项(Oracle Solaris Studio 12.4 之前编译器版本的默认模型)生效,但 遵循 definitions-separate 模型,可能多次编译 foo.cc 文件,从而导致编译和链接时出现多重定义错误。
  • 如果 -template=no%extdef 选项(Oracle Solaris Studio 12.4 编译器的默认模型)生效,但 遵循 definitions-separate 模型,可能从不编译 foo.cc 文件,从而可能在编译和链接时出现定义缺失错误。
  • 好吧,有三种可能。偶尔代码可能编译和链接,但稍后更改源代码时,还是可能失败。

Apache stdcxx 库的问题

截至本文撰写时,通过 -library=stdcxx4 选项选择的 Oracle Solaris 版本的 Apache stdcxx 库与新的默认选项 -template=no%extdef 不兼容。您在编译时可能也会看到以 Warning: Could not find source for __rw:: 开头的警告,涉及库的内部。您在链接时可能会看到缺少名称中包括 __rw:: 的符号的错误。

在推出该库的更新版本之前,您可能需要向具有 -library=stdcxx4 的 CC 命令行添加 -template=extdef

您需要做出哪些更改?

如果显式使用任一 -template=[no%]extdef 选项,则无需任何更改。Oracle Solaris Studio 12.4 中的唯一不同在于未指定这两个选项中任一者时的默认编译器行为。

如果您完全使用 Oracle Solaris Studio C++ 开发代码,或者该代码依赖于 definitions-separate 模板模型,可能需要在 CC 命令行添加选项 -template=extdef,以便恢复到 Oracle Solaris 12.3 及更低版本编译器的行为。

您还可以考虑修改源代码的组织方式,使其适用于任一模板编译模型。上面的清单 2 是适用任一模型的代码示例,其模板定义在头文件中且没有对应的 .cc 文件。

最后,这些选项只影响 C++ 程序的编译。它们不影响成功编译后 C++ 程序的含义。

另请参见

Oracle Solaris Studio 12.4:C++ 用户指南

关于作者

Steve Clamage 是 Oracle Solaris Studio 团队的高级工程师,并且是 Oracle C++ 编译器的项目主管。他从 1996 年开始一直担任美国 C++ 标准委员会的主席。

修订版 1.0,2014 年 9 月 12 日

关注我们:
博客 | Facebook | Twitter | YouTube