本章介绍了与此编译器相关的语言扩展。另外,A.2.182 -z[ ]arg中也提供了与实现相关的信息。在命令行上指定某些编译器选项之后,编译器才能识别本章中描述的某些功能。相关编译器选项在相应章节中列出。
使用 -features=extensions 选项可以编译其他 C++ 编译器通常接受的非标准代码。必须编译无效代码且不允许修改代码而使之有效时,您可以使用该选项。
本章介绍了使用 -features=extensions 选项时编译器支持的语言扩展。
可以很容易的将每个支持无效代码的实例转变为所有编译器接受的有效代码。如果允许使代码有效,那么您应该使代码有效而不是使用该选项。使用 -features=extensions 选项可以使某些编译器拒绝的无效代码永远存在。
可使用下列声明说明符来协助约束外部符号的声明和定义。文件链接到共享库或可执行文件之前,静态归档或目标文件指定的作用域限制不会生效。尽管如此,编译器仍然可以执行显示链接程序作用域说明符的某些优化。
通过使用这些说明符,您不必再使用链接程序作用域的 mapfile。也可以通过在命令行上指定-xldscope 来控制变量作用域的缺省设置。
有关更多信息,请参见A.2.133 -xldscope={v}。
表 4–1 链接程序作用域声明说明符
符号定义可以用更多限制的说明符来重新声明,但是不可以用较少限制的说明符重新声明。符号定义后,不可以用不同的说明符声明符号。
__global 是限制最少的作用域,__symbolic 是限制较多的作用域,而 __hidden 是限制最多的作用域。
因为虚函数的声明影响虚拟表的结构和解释,所以所有虚函数对包括类定义的所有编译单元必须是可视的。
可以将链接程序作用域说明符应用于结构、类和联合声明和定义中,因为 C++ 类可能要求生成隐式信息,如虚拟表和运行时类型信息。在这种情况下,说明符后跟结构、类或联合关键字。这种应用程序为其所有隐式成员隐含了相同的链接程序作用域。
可以通过声明线程局部变量利用线程局部存储。线程局部变量声明普通变量声明与声明说明符 __thread 组成。有关更多信息,请参见A.2.173 -xthreadvar[= o]。
必须将 __thread 说明符包括在第一个线程变量声明中。使用 __thread 说明符声明的变量的绑定方式与没有 __thread 说明符时相同。
只能使用 __thread 说明符声明静态持续时间的变量。具有静态持续时间的变量包括了文件全局、文件静态、函数局部静态和类静态成员。不能使用 __thread 说明符声明动态或自动持续时间的变量。线程变量可以具有静态初始化函数,但是不可以具有动态初始化函数或析构函数。例如,允许 __thread int x = 4;,但不允许 __thread int x = f();。线程变量不能包含具有重要构造函数和析构函数的类型。具体来说,就是线程变量的类型不能为 std::string。
运行时对线程变量的地址运算符 (&) 求值并返回当前线程变量的地址。因此,线程变量的地址不是常量。
线程变量的地址在相应线程的生命周期中是稳定的。进程中任何线程都可以在线程变量的生命周期任意使用该变量的地址。不能在线程终止后使用线程变量的地址。线程变量的所有地址在线程终止后都是无效的。
C++ 标准规定,覆盖虚拟函数在异常中允许的限制不得低于它覆盖的任何函数的限制。该虚函数可能与覆盖的任何函数具有相同或更多的限制。注意,不存在异常规范也允许任何异常。
例如,假定通过指向基类的指针调用函数。如果函数具有异常规范,则可以计算出没有其他正抛出的异常。如果覆盖函数具有限制较少的规范,则不可预料的异常可能会被抛出,这会导致奇怪的程序行为并且终止程序。这就是规则的原因。
使用 -features=extensions 时,编译器允许覆盖异常规范限制较小的函数。
使用 -features=extensions 时,编译器允许对 enum 类型和变量进行前向声明。此外,编译器允许声明不完整 enum 类型的变量。编译器总是假定不完整 enum 类型的大小和范围与当前平台上的 int 类型相同。
以下是两行无效代码示例,如果使用 -features=extensions 选项,可对其进行编译。
enum E; // invalid: forward declaration of enum not allowed E e; // invalid: type E is incomplete |
因为 enum 定义不能互相引用,并且 enum 定义不能交叉引用另一种类型,所以从来不必对枚举类型进行前向声明。要使代码有效,可以总是先提供 enum 的完整定义,然后再使用它。
在 64 位体系结构上,enum 要求的大小可能比 int 类型大。如果是这种情况,并且如果向前声明和定义在同一编译中是可视的,那么编译器将发出错误。如果实际大小不是假定的大小并且编译器没有发现这个差异,那么代码将编译并链接,但有可能不能正常运行。可能出现奇怪的程序行为,尤其是 8 字节值存储在 4 字节变量中时。
使用 -features=extensions 时,不完整的 enum 类型以前向声明处理。例如,以下是无效代码,如果使用 -features=extensions 选项,可对其进行编译。
typedef enum E F; // invalid, E is incomplete |
如前所述,可以总是先包括 enum 类型的定义,然后再使用。
因为 enum 声明并不引入作用域,所以 enum 名称不能作为作用域限定符。例如,以下代码是无效的。
enum E {e1, e2, e3}; int i = E::e1; // invalid: E is not a scope name |
要编译该无效代码,请使用 -features=extensions 选项。-features=extensions 选项指示编译器在作用域限定符是 enum 类型的名称的情况下忽略该作用域限定符。
要使代码有效,请删除无效的限定符 E::。
使用该选项提高了排字错误的可能性,产生了编译没有错误消息的错误程序。
匿名结构声明是既不声明结构标记也不声明对象或 typedef 名称的声明。C++ 中不允许匿名结构。
-features=extensions 选项允许使用匿名 struct 声明,但仅作为联合的成员。
以下代码是无效匿名 struct 声明示例,如果使用 -features=extensions 选项,可对其进行编译。
union U { struct { int a; double b; }; // invalid: anonymous struct struct { char* c; unsigned d; }; // invalid: anonymous struct }; |
struct 成员的名称是可视的,没有 struct 成员名称的限定。如果该代码示例中提供了 U 的定义,则可以编写:
U u; u.a = 1; |
匿名结构与匿名联合服从相同的限制。
请注意,可以通过为每个 struct 提供一个名称以使代码有效,如:
union U { struct { int a; double b; } A; struct { char* c; unsigned d; } B; }; U u; U.A.a = 1; |
不允许获取临时变量的地址。例如,因为以下代码获取了构造函数调用创建的变量地址,所以这些代码是无效的。但是,如果使用 -features=extensions 选项,编译器将接受该无效代码。
class C { public: C(int); ... }; void f1(C*); int main() { f1(&C(2)); // invalid } |
注意,可以通过使用显式变量来使该代码有效。
C c(2); f1(&c); |
函数返回时,临时对象被销毁。程序员应确保临时变量的地址没有留下。此外,销毁临时变量(例如 f1)时,临时变量中存储的数据会丢失。
下面的代码是无效的:
class A { friend static void foo(<args>); ... }; |
因为类名具有外部链接并且所有定义必须是相等的,所以友元函数也必须具有外部链接。但是,如果使用 -features=extensions 选项,编译器将接受该代码。
程序员处理该无效代码的方法大概是在类 A 的实现文件中提供非成员 "helper" 函数。可以通过使 foo 成为静态成员函数得到相同效果。如果不要客户端调用函数,则可以使该函数私有化。
如果使用该扩展,则任何客户端都可以“劫取”您的类。任何客户端都可以包括类的头文件,然后定义其自身的静态函数 foo,该函数将自动成为类的友元。结果就好像是您使类的所有成员成为了公共的。
使用 -features=extensions 时,编译器将每个函数中的标识符 __func__ 隐式声明为静态 const char 数组。如果程序使用标识符,编译器还会提供以下定义,其中,function-name 是函数原始名称。类成员关系、名称空间和重载不反映在名称中。
static const char __func__[] = "function-name"; |
例如,请考虑以下代码段。
#include <stdio.h> void myfunc(void) { printf("%s\n", __func__); } |
每次调用函数时,函数将把以下内容打印到标准输出流。
myfunc |