C H A P T E R  4

Language Extensions

This chapter documents the language extensions specific to this compiler. Appendix B also provides implementation specific information. The compiler does not recognize some of the features described in this chapter unless you specify certain compiler options on the command line. The relevant compiler options are listed in each section as appropriate.

The -features=extensions option enables you to compile nonstandard code that is commonly accepted by other C++ compilers. You can use this option when you must compile invalid code and you are not permitted to modify the code to make it valid.

This chapter describes the language extensions that the compiler supports when you use the -features=extensions options.

Note - You can easily turn each supported instance of invalid code into valid code that all compilers will accept. If you are allowed to make the code valid, you should do so instead of using this option. Using the -features=extensions option perpetuates invalid code that will be rejected by some compilers.

4.1 Linker Scoping

Use the following declaration specifiers to help constrain declarations and definitions of extern symbols. The scoping restraints you specify for a static archive or an object file will not take effect until the file is linked into a shared library or an executable. Despite this, the compiler can still perform some optimization given the presence of the linker scoping specifiers.

By using these specifiers, you no longer need to use mapfiles for linker scoping. You can also control the default setting for variable scoping by specifying -xldscope on the command line.

For more information, see Section A.2.128, -xldscope={v}.

TABLE 4-1 Linker Scoping Declaration Specifiers




Symbol definitions have global linker scoping and is the least restrictive linker scoping. All references to the symbol bind to the definition in the first dynamic load module that defines the symbol. This linker scoping is the current linker scoping for extern symbols.


Symbol definitions have symbolic linker scoping and is more restrictive than global linker scoping. All references to the symbol from within the dynamic load module being linked bind to the symbol defined within the module. Outside of the module, the symbol appears as though it were global. This linker scoping corresponds to the linker option -Bsymbolic. Although you cannot use -Bsymbolic with C++ libraries, you can use the __symbolic specifier without causing problems. See ld(1) for more information on the linker.


Symbol definitions have hidden linker scoping. Hidden linker scoping is more restrictive than symbolic and global linker scoping. All references within a dynamic load module bind to a definition within that module. The symbol will not be visible outside of the module.

A symbol definition may be redeclared with a more restrictive specifier, but may not be redeclared with a less restrictive specifier. A symbol may not be declared with a different specifier once the symbol has been defined.

__global is the least restrictive scoping, __symbolic is more restrictive, and __hidden is the most restrictive scoping.

All virtual functions must be visible to all compilation units that include the class definition because the declaration of virtual functions affects the construction and interpretation of virtual tables.

You can apply the linker scoping specifiers to struct, class, and union declarations and definitions because C++ classes may require generation of implicit information, such as virtual tables and run-time type information. The specifier, in this case, follows the struct, class, or union keyword. Such an application implies the same linker scoping for all its implicit members.

4.2 Thread-Local Storage

Take advantage of thread-local storage by declaring thread-local variables. A thread-local variable declaration consists of a normal variable declaration with the addition of the declaration specifier __thread. For more information, see Section A.2.168, -xthreadvar[=o].

You must include the __thread specifier in the first declaration of the thread variable. Variables that you declare with the __thread specifier are bound as they would be without the __thread specifier.

You can declare variables only of static duration with the __thread specifier. Variables with static duration include file global, file static, function local static, and class static member. You should not declare variables with dynamic or automatic duration with the __thread specifier. A thread variable can have a static initializer, but it cannot have a dynamic initializer or destructors. For example, __thread int x = 4; is permitted, but __thread int x = f(); is not. A thread variable should not have a type with non-trivial constructors and destructors. In particular, a thread variable may not have type std::string.

The address-of operator (&) for a thread variable is evaluated at run time and returns the address of the current thread's variable. Therefore, the address of a thread variable is not a constant.

The address of a thread variable is stable for the lifetime of the corresponding thread. Any thread in the process can freely use the address of a thread variable during the variable's lifetime. You cannot use a thread variable's address after its thread terminates. All addresses of a thread's variables are invalid after the thread's termination.

4.3 Overriding With Less Restrictive Virtual Functions

The C++ standard says that an overriding virtual function must not be less restrictive in the exceptions it allows than any function it overrides. It can have the same restrictions or be more restrictive. Note that the absence of an exception specification allows any exception.

Suppose, for example, that you call a function through a pointer to a base class. If the function has an exception specification, you can count on no other exceptions being thrown. If the overriding function has a less-restrictive specification, an unexpected exception could be thrown, which can result in bizarre program behavior followed by a program abort. This is the reason for the rule.

When you use -features=extensions, the compiler will allow overriding functions with less-restrictive exception specifications.

4.4 Making Forward Declarations of enum Types and Variables

When you use -features=extensions, the compiler allows the forward declaration of enum types and variables. In addition, the compiler allows the declaration of a variable with an incomplete enum type. The compiler will always assume an incomplete enum type to have the same size and range as type int on the current platform.

The following two lines show an example of invalid code that will compile when you use the -features=extensions option.

enum E; // invalid: forward declaration of enum not allowed
E e;    // invalid: type E is incomplete

Because enum definitions cannot reference one another, and no enum definition can cross-reference another type, the forward declaration of an enumeration type is never necessary. To make the code valid, you can always provide the full definition of the enum before it is used.

Note - On 64-bit architectures, it is possible for an enum to require a size that is larger than type int. If that is the case, and if the forward declaration and the definition are visible in the same compilation, the compiler will emit an error. If the actual size is not the assumed size and the compiler does not see the discrepancy, the code will compile and link, but might not run properly. Mysterious program behavior can occur, particularly if an 8-byte value is stored in a 4-byte variable.

4.5 Using Incomplete enum Types

When you use -features=extensions, incomplete enum types are taken as forward declarations. For example, the following invalid code will compile when you use the -features=extensions option.

typedef enum E F; // invalid, E is incomplete

As noted previously, you can always include the definition of an enum type before it is used.

4.6 Using an enum Name as a Scope Qualifier

Because an enum declaration does not introduce a scope, an enum name cannot be used as a scope qualifier. For example, the following code is invalid.

enum E {e1, e2, e3};
int i = E::e1; // invalid: E is not a scope name

To compile this invalid code, use the -features=extensions option. The -features=extensions option instructs the compiler to ignore a scope qualifier if it is the name of an enum type.

To make the code valid, remove the invalid qualifier E::.

Note - Use of this option increases the possibility of typographical errors yielding incorrect programs that compile without error messages.

4.7 Using Anonymous struct Declarations

An anonymous struct declaration is a declaration that declares neither a tag for the struct, nor an object or typedef name. Anonymous structs are not allowed in C++.

The -features=extensions option allows the use of an anonymous struct declaration, but only as member of a union.

The following code is an example of an invalid anonymous struct declaration that compiles when you use the -features=extensions option.

union U {
  struct {
    int a;
    double b;
  };  // invalid: anonymous struct
  struct {
    char* c;
    unsigned d;
  };  // invalid: anonymous struct

The names of the struct members are visible without qualification by a struct member name. Given the definition of U in this code example, you can write:

U u;
u.a = 1;

Anonymous structs are subject to the same limitations as anonymous unions.

Note that you can make the code valid by giving a name to each struct, such as:

union U {
  struct {
    int a;
    double b;
  } A;
  struct {
    char* c;
    unsigned d;
  } B;
U u;
U.A.a = 1;

4.8 Passing the Address of an Anonymous Class Instance

You are not allowed to take the address of a temporary variable. For example, the following code is invalid because it takes the address of a variable created by a constructor call. However, the compiler accepts this invalid code when you use the -features=extensions option.

class C {
void f1(C*);
int main()
  f1(&C(2)); // invalid

Note that you can make this code valid by using an explicit variable.

C c(2);

The temporary object is destroyed when the function returns. Ensuring that the address of the temporary variable is not retained is the programmer's responsibility. In addition, the data that is stored in the temporary variable (for example, by f1) is lost when the temporary variable is destroyed.

4.9 Declaring a Static Namespace-Scope Function as a Class Friend

The following code is invalid.

class A {
  friend static void foo(<args>);

Because a class name has external linkage and all definitions must be identical, friend functions must also have external linkage. However, when you use the -features=extensions option, the compiler to accepts this code.

Presumably the programmer's intent with this invalid code was to provide a nonmember "helper" function in the implementation file for class A. You can get the same effect by making foo a static member function. You can make it private if you do not want clients to call the function.

Note - If you use this extension, your class can be "hijacked" by any client. Any client can include the class header, then define its own static function foo, which will automatically be a friend of the class. The effect will be as if you made all members of the class public.

4.10 Using the Predefined __func__ Symbol for Function Name

When you use -features=extensions, the compiler implicitly declares the identifier __func__ in each function as a static array of const char. If the program uses the identifier, the compiler also provides the following definition where function-name is the unadorned name of the function. Class membership, namespaces, and overloading are not reflected in the name.

static const char __func__[] = "function-name";

For example, consider the following code fragment.

#include <stdio.h>
void myfunc(void)
  printf("%s\n", __func__);

Each time the function is called, it will print the following to the standard output stream.