Oracle® Developer Studio 12.5:C++ 用户指南

退出打印视图

更新时间: 2016 年 7 月
 
 

1.1 Oracle Developer Studio 12.5 C++ 5.14 编译器的新增特性和功能

本节提供了在 Oracle Developer Studio 12.5 C++ 5.14 编译器发行版中引入的新增特性和已更改功能的摘要列表。

  • 支持 SPARC M6、M7 和 T7 处理器。

  • 支持 Intel Broadwell/avx2_i 处理器。

  • 支持 x86 数据空间分析。

  • Oracle Solaris 上的缺省编译模式是使用 –library=Cstd–compat=5(使用 Sun ABI 和 libCstd 库的 C++03 模式)。Oracle Linux 上的缺省编译模式是 –std=c++03(使用 g++ ABI 和运行时库的 C++03 模式)。

  • 在所有平台上的 –compat=5 模式下,现在,缺省值是正确的名称改编。以前,名称改编中的错误是 SPARC 上的缺省值和使用 –m32 的 x86 上的缺省值。

  • 新编译器选项:

    • –abiopt=[mangle5|mangle6] 仅在 –compat=5 模式下可用。缺省值是 mangle6,即正确的名称改编。在 Oracle Solaris SPARC 上以及使用 –m32 的 Oracle Solaris x86 上,可以指定 mangle5 以便与早期编译器中可能有错误的名称改编兼容。

    • –features=[no%]mergestrings 可使编译器将字符串文字以及其他合适的 const 或只读数据放入二进制文件的一个特殊部分中,链接程序将从中删除重复的字符串。此选项仅在 SPARC 上可用。

    • –pedantic 针对缺省情况下接受的但未遵循 C++ 标准的代码发出警告或错误。

    • –xatomic 指定链接哪个原子支持运行时库。

    • –xcheck=noreturn 通知编译器当已描述为“不返回”的例程返回时添加代码来导致运行时错误。

    • –xsecure_code_analysis 启用编译器安全代码分析,以便在编译时查找和显示可能的内存安全违规。

1.1.1 C++11 标准

Oracle Developer Studio 12.5 提供对 C++11 的完全支持。

随 Oracle Solaris Studio 12.4 提供的 g++ 4.8.2 库未遵循 C++11 的所有要求。具体而言,标准字符串和列表类需要进行更改,这会破坏二进制兼容性。

g++ 5.1 发行版可以在与 g++ 4.x 兼容或遵循 C++ 标准之间选择。这些选择互不兼容。文章 GCC5 和 C++11 ABI 中提供了有关使用 g++ 的更详细信息。

–std=c++11–std=c++14 模式下的 Oracle Developer Studio 12.5 提供了与 Oracle Solaris Studio 12.4 和 g++ 4.8.2 的兼容性。Oracle Developer Studio 12.5 未提供不兼容的遵循标准的选项。

如果要将 g++ 5.1 二进制文件与 Oracle Developer Studio 二进制文件组合在一起,必须在每个 g++ 命令行上使用 –D_GLIBCXX_USE_CXX11_ABI=0 选项编译所有 g++ 二进制文件。

1.1.2 C++14 标准

Oracle Developer Studio 12.5 提供对 C++14 标准的有限支持。支持以下功能:

  • 二进制文字

  • 固定大小的取消分配

  • deprecated 属性

  • 单引号数字分隔符

  • 成员初始化和聚集

1.1.3 对原子的支持

原子是 C++ 2011 标准中新增的一种语言功能。原子功能仅对 –std=c++11–std=c++14 选项可用。有关支持的函数和类型的信息,请参见 C++ 语言标准以及 atomic_fence(3A)atomic_flag(3A)stdatomic.h(3A) 手册页。

原子功能需要运行时支持。有关运行时库的更多信息,请参见捆绑的原子库

1.1.4 C++ 规则的实施

Oracle Developer Studio 12.5 C++ 5.14 编译器比过去的编译器更严格地强制执行某些 C++ 规则。规则实施错误导致早期的编译器无法正确处理某些有效代码。本节说明更严格的实施,提供错误代码的示例,并显示如何修复代码。在所有这些例子中,您都可以更改代码,随后该代码应在新旧编译器中正常运行。

1.1.4.1 模板定义解析

以前的编译器不解析模板定义,直到实例化模板后才会进行解析,这会产生以下后果:

  1. 以前的编译器直到定义模板后才允许使用未声明的名称。这可能会因明显的冲突而导致无效代码被接受,有效代码被拒绝,或使用错误的声明。

  2. 无法诊断出未实例化的模板中的无条件错误。

  3. 只有在实例化模板 T 后,才会实例化模板 T 的定义中不依赖于模板参数的名称。这些名称应在定义 T 时隐式实例化。

示例 1:

template< class T > int f(int i = j) // j is not visible
  { return i; }
int j = 2;
int main()
{
    return f<int>();
}

早期的编译器接受此代码,因为只有在定义了 j 后才会解析模板 f

解决方法:在定义模板之前声明 j

示例 2:

#include <stdio.h>
void f(double d) { printf("f(double)\n"); }
template< class C > struct B {
    B() { f(1); } // f is not dependent on template parameter
};
void f(int d) { printf("f(int)\n"); }
int main()
{
    B<int> b; // should print "f(double)"
}

解决方法:确保在定义模板之前进行模板所依赖的任何声明。

1.1.4.2 相关基本查找

在模板定义中查找未限定的名称时,不应检查依赖于模板参数的基类。以前的编译器对名称的查找不正确。

示例:

template <typename T> struct Base { };

template <typename T> struct Derived : Base <T>
{
    Derived() : Base() { } // unqualified Base should not be found
};
Derived <int> x;

template <typename T> struct Derived2 : Base <T>
{
    Derived2() : Base<T>() { } // OK
};
Derived2<int> x2;

int main() { }

在类 Derived 中,使用未限定的 Base 而没有模板参数是无效的。

解决方法:在类 Derived2 中,这种用法是正确的。

1.1.4.3 重新声明模板参数

您不能再重新声明模板参数的名称。

示例:

template <typename T> class A { typedef int T; }; // re-declare T

解决方法:为模板参数或本地名称选择其他名称。

1.1.4.4 前向模板声明

不再支持前向模板声明。

示例:

$ cat x.cc
template <> struct A<int>;
.
$ CC x.cc
"x.cc", line 1: Error: A class template name was expected instead of A<int>.

Oracle Developer Studio C++ 的以前发行版会无提示地将此接受为由具有一个类型参数的名称 "A" 对隐式声明的模板的显式专门化。

解决方法:在任何显式专门化之前声明主要模板。

template<class T> struct A;

template <> struct A<int>;

1.1.4.5 旧样式显式实例化

不再允许使用 1998 之前的不带 template<> 的显式专门化声明样式。

示例:

template <typename T> class A { static T m; };
int A<int>::m = 0; // now an error

解决方法:

template<> int A<int>::m = 0;

1.1.4.6 更严格的 -template=extdef

请参阅独立的模板定义。“独立模板定义”编译模型现在更严格了。如果要创建的 X.cc 文件包含在头文件 X.h 中声明的模板的定义,必须特别小心,不要在该文件中添加其他任何内容(即,除了与定义直接相关的项目之外的任何内容)。现在如果违反了此规则,则可能得到“多个定义”错误。

示例:

% cat extdef.h
template <typename T> T foo(T);
% cat extdef.cc
#include "extdef.h"
int main() {
        foo(1);
}
% CC extdef.cc -template=extdef
"extdef.cc", line 3: Error: main() already had a body defined.
1 Error(s) detected.

解决方法:从 X.cc 文件中删除模板定义不需要的任何内容。请记得 X.cc 文件无需显式编译,因为只要包括 X.h 文件,就会自动包括它。

如果需要进行其他更改,可考虑使用 -template=no%extdef 选项进行编译。此行为是其他编译器的缺省行为,现在也是此发行版的缺省行为。

1.1.4.7 隐式 int

在 C++ 中从不允许隐式声明类型 int,但早期的编译器有时允许声明并显示警告。由于这干扰了正常的模板处理过程,因此编译器不再假定您要声明类型 int

示例:

static i = 0; // now an error

解决方法:在声明中显式提供类型。

1.1.4.8 友元声明

如果类 C 中函数的友元声明或类 T 是 T 的第一个声明,以前的编译器会错误地将 T 的声明插入周围作用域。Oracle Developer Studio 12.5 C++ 5.14 编译器不再这样做,因为它可能会导致对有效程序的错误解释。

示例:

class A
{
    friend class B;    // not previously declared
    friend void foo(); // not previously declared

    B* bar() // Error: B is not defined.
    {
        foo(); // Error: The function "foo" must have a prototype.
        return 0;
    }
};

解决方法:在将类声明为友元的类声明之前,在该类之外的作用域中声明友元函数或类。

class B;
void foo();
class A
{
    friend class B;    // refers to prior declaration
    friend void foo(); // refers to prior declaration

    B* bar() // OK
    {
        foo(); // OK
        return 0;
    }
};

指定了缺省参数的友元声明还必须是函数定义。

示例:

class C {
    friend int f1(int i = 0); // Error
    friend int f2(int i = 0) { return i; } // OK
}

C++ 编译器的以前发行版不强制执行此规则。允许在友元声明中使用缺省参数会干扰正常的模板处理,因此不再允许这样做。

1.1.4.9 模板中的静态函数和名称查找

  1. 在相关函数调用中查找名称时,以前的编译器会错误地忽略作用域中的静态函数。Oracle Developer Studio 12.5 C++ 5.14 编译器现在以相同方式处理 staticextern 函数。

    示例:

    // previous compiliers ignored this bar() in dependent name lookup
    static int bar(int)
    {
            return 1;
    }
    int bar(long)
    {
            return 0;
    }
    template <typename T>
    int foo(T t)
    {
    	// function call depends on template argument
            return bar(t);
    }
    int main()
    {
            return foo(0);
    }

    使用以前的编译器编译时,程序会返回 0。使用 Oracle Developer Studio 12.5 C++ 5.14 编译器时,程序会返回 1。

  2. 如果在名称查找中只找到静态函数,以前的编译器会发出类似如下的错误

    "Reference to static bar(int) not allowed in template foo(int), 
    try using -features=tmplrefstatic."

    请参见前面的示例,删除 extern 函数 bar(long)。编译器现在只在 -compat=5 模式下发出警告,因为该代码在技术上是对 C++03 标准的扩展。在 C++11 模式中,将接受此代码而不显示任何提示,因为它是有效的 C++11。

  3. 仍接受选项 -features=no%tmplrefstatic,但它没有实际作用,因为始终允许对模板中的静态对象的引用。

1.1.4.10 const 正确性

以前的编译器不强制执行可保留关键字 const 的属性的所有规则。

示例:

void f1(int**) { printf("f1(int**)\n"); }
void f2(int**) { printf("Error: f2(int**)\n"); }
void f2(...)   { printf("OK: f1(...)\n"); }
int main()
{
  int* const p = 0;
  f1(&p);         // #1 Should be an error
  f2(&p);         // #2 Should choose f2(...)
}

早期的编译器错误地接受了 #1,而对于 #2,则错误地调用了 f2(int**)。如第 2 行所示,由于编译器的错误行为所致,可能会选择错误的重载函数或错误的模板专门化。

可以修复第 1 行,方法是声明

int* p = 0;

或者更改 f1 的定义或添加重载

void f1(int * const*);

如果您希望现有的无效代码继续编译,则可以将未编档的选项

-W0,-xconstcheck=false [note: W-zero, not W-oh]

添加到 CC 命令行,该命令行现在生成有关 const 正确性的警告消息。