This chapter documents the language extensions specific to this compiler. 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.
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.
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 A.2.136 -xldscope={v}.
Table 4–1 Linker Scoping Declaration Specifiers
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.
For compatibility with similar scoping features in Microsoft Visual C++ (MSVC++) for dynamic libraries, the following syntax is also supported:
__declspec(dllexport) is equivalent to __symbolic |
__declspec(dllimport) is equivalent to __global |
When taking advantage of this syntax with Sun C++, you should add the option -xldscope=hidden to CC command lines. The result will be comparable to the results using MSVC++. With MSVC++, __declspec(dllimport) is supposed to be used only on declarations of external symbols, not on definitions. Example:
__declspec(dllimport) int foo(); // OK __declspec(dllimport) int bar() { ... } // not OK |
MSVC++ is lax about allowing dllimport on definitions, and the results using Sun C++ will be different. In particular, using dllimport on a definition using Sun C++ results in the symbol having global linkage instead of symbolic linkage. Dynamic libraries on Microsoft Windows do not support global linkage of symbols. If you run into this problem, you can change the source code to use dllexport instead of dllimport on definitions. You will then get the same results with MSVC++ and Sun C++.
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 A.2.182 -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.
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.
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.
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.
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.
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::.
Use of this option increases the possibility of typographical errors yielding incorrect programs that compile without error messages.
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; |
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 { public: C(int); ... }; 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); f1(&c); |
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.
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.
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.
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.
myfunc |
This attribute, attached to struct or union type definition, specifies that each member (other than zero-width bitfields) of the structure or union is placed to minimize the memory required. When attached to an enum definition, it indicates that the smallest integral type should be used.
Specifying this attribute for struct and union types is equivalent to specifying the packed attribute on each of the structure or union members.
In the following example struct my_packed_struct's members are packed closely together, but the internal layout of its s member is not packed. To do that, struct my_unpacked_struct would also need to be packed.
struct my_unpacked_struct { char c; int i; ; struct __attribute__ ((__packed__)) my_packed_struct { char c; int i; struct my_unpacked_struct s; }; |
You may only specify this attribute on the definition of a enum, struct or union, and not on a typedef that does not also define the enumerated type, structure or union.