United States |
Previous | Contents | Index |
Compaq C++ implements the C++ language as defined in The Annotated C++ Reference Manual, and closely follows that text's language definition.
This chapter describes ways to avoid having the Compaq C++ compiler reject program code that previously worked with other C++ implementations that adhere less strictly to the C++ language definition. References to applicable portions of The C++ Programming Language, 2nd Edition indicate where you can find additional help.
For compatibility with certain C++ compilers, Compaq C++ supports the
-cfront
and
-ms
options to the
cxx
command. These option direct the compiler to interpret the source
program according to certain rules followed by other implementations of
the C++ language. For more information, see Section 2.1.
4.1 Using Classes
This section discusses porting issues pertaining to C++ classes.
4.1.1 Friend Declarations
When making friend declarations, use the elaborated form of type specifier. The following code fragment implements the legal and comments out the illegal friend declaration:
class Y; class Z; class X; //friend Y; ** not legal friend class Z; // legal }; |
Compaq C++ enforces accessibility rules for
public
,
protected
, and
private
members of a base class. For more information, see §r.11.2 of
The C++ Programming Language, 2nd Edition.
4.1.3 Base Class Initializers
Unlike some older C++ implementations, Compaq C++ requires you to use the base class name in the initializer for a derived class. The following code fragment implements a legal initializer and comments out an illegal initializer:
class Base { // ... public: Base (int); }; class Derived : public Base { // ... public: // Derived(int i) : (i) {/* ...*/} ** not legal Derived(int i) : Base(i) {/* ...*/} // ** legal, supplies class name }; |
For more information, see §r.12.6.2 and §r.18.3.2 of
The C++ Programming Language, 2nd Edition.
4.2 Undefined Global Symbols for Static Data Members
When a static data member is declared, the Compaq C++ compiler issues a reference to the external identifier in the object code, which must be resolved by a definition. On Tru64 UNIX systems, the Compaq C++ compiler does not support the declaration anachronism shown in §r.18.3 of The C++ Programming Language, 2nd Edition.
For example, consider the following code fragment:
int main () { int x; x=C::i; return 0; } |
The Compaq C++ compiler does not issue any messages during compilation; however, when you attempt to link a program containing this code, the linker issues a message similar to the following:
ld: Error: Undefined: i__1C |
Compaq C++ requires the use of function definitions as described in §r.8.3 of The C++ Programming Language, 2nd Edition. For examples of outdated syntax not allowed in Compaq C++, see §r.18.3.1 of The C++ Programming Language, 2nd Edition.
Because all linkage specifications for a name must agree, function prototypes are not permitted if the function is later declared as an inline function. The following code is an example of such a conflicting function declaration:
int f(); inline int f() { return l; } |
In this example, f is declared with both internal and external linkage, which causes a compiler error.
Similarly, the declaration
int f(i,j)
causes an error even when the declaration is defined with the
"C"
linkage specification, because the linkage specification has no effect
on the semantics of the declaration.
4.4 Using Pointers
This section demonstrates how to use pointers effectively in
Compaq C++.
4.4.1 Pointer Conversions
In Compaq C++, you cannot implicitly convert a const pointer to a nonconstant pointer. For example, char * and const char * are not equivalent; explicitly performing such a cast can lead to unexpected results.
For more information, see §r.4.6 of The C++ Programming Language, 2nd Edition.
4.4.2 Bound Pointers
Binding a pointer to a member function with a particular object as an
argument to the function is not allowed in Compaq C++. For more
information on the illegality of casting bound pointers, see
§18.3.4 of The C++ Programming Language, 2nd Edition.
4.4.3 Constants in Function Returns
Because the return value cannot be an lvalue, a constant in a function return has no effect on the semantics of the return. However, using a constant in a function return does affect the type signature. For example:
static int f1( int a, int b) {;} const int (* const (f2[])) (int a, int b) = {f1}; |
In this example, the referenced type of the pointer value f1 in the initializer for f2[] is function (signed int, signed int) , which returns signed int . This is incompatible with function (signed int, signed int) , which returns const signed int .
You can omit the
const
of
int
because it affects only the constant return signature.
4.4.4 Pointers to Constants
The following example shows a type mismatch between a pointer to a char and a pointer to a const char that some compilers, other than the Compaq C++ compiler, may not find:
void foo (const char* argv[]) {} int main() { static char* args[2] = {"foo","bar"}; /* 'In this statement, the referenced type of the pointer value "args" is "pointer to char"' which is not compatible with "pointer to const char"'*/ foo (args); return 0; } |
You can correct this example by changing
static char
to
static const char
. Use an explicit type cast to get an argument match only if no other
option is available; such a cast may break on some C++ implementations.
4.5 Using typedefs
Using a synonym after a class , struct , or union prefix is illegal. Using a synonym in the names for constructors and destructors within the class declaration itself is also illegal.
In the following example, the illegal typedef specifier is commented out:
typedef struct { /* ...*/ } foo; // typedef struct foo foobar; ** not legal |
For more information, see §r.7.1.3 of The C++ Programming Language, 2nd Edition.
4.6 Initializing References
Compaq C++ warns against initializing nonconstant references to refer to temporary objects. The following example demonstrates the problems that can result:
static void f() { int i = 5; i++; // OK int &ri = 23; ri++; // In the initializer for ri, the initialization of a // non-const reference requires a temporary for "23". } |
The issue of reference initialization arises most often in assignment operators and in copy constructors. Wherever possible, declare all reference arguments as const .
For more information, see §r.8.4.3 of The C++ Programming Language, 2nd Edition.
4.7 Using the switch and goto Statements
Branching around a declaration with an explicit or implicit initializer is not legal, unless the declaration is in an inner block that is completely bypassed. To satisfy this constraint, enclose the declaration in a block. For example:
int i; switch (i) { case 1: int l = 0; //not initialized at this case label myint m = 0; //not initialized at this case label { int j = 0; // legal within the braces myint m = 0; // legal within the braces } case 2: break; // ... } |
For more information on using the
switch
statement, see §r.6.4.2 of The C++ Programming Language, 2nd Edition.
4.8 Using Volatile Objects
You must supply the meaning of copy constructing and assigning from volatile objects, because the compiler generates no copy constructors or assignment operators that copy or assign from volatile objects. The following example contains examples of such errors, as noted in the comments:
class A { public: A() { } // A(volatile A&) { } // operator=(volatile A&) { return 0; } }; void foo() { volatile A va; A a; A cca(va); // error - cannot copy construct from volatile object a = va; // error - cannot assign from volatile object return; } |
For more information, see §r.7.1.6 of The C++ Programming Language, 2nd Edition.
4.9 Preprocessing
Compaq C++ allows identifiers, but not expressions, on the #ifdef preprocessor directive. For example:
// this is not legal // #ifdef KERNEL && !defined(__POSIX_SOURCE) |
The following is the legal alternative:
// use this instead #if defined(KERNEL) && !defined(__POSIX_SOURCE) |
For more information, see §r.16.5 of The C++ Programming Language, 2nd Edition.
4.10 Managing Memory
The proper way to manage memory for a class is to overload the new and delete operators. This is in contrast to some older C++ implementations, which let you manage memory through assignment to the this pointer.
For more information, see §r.18.3.3 of The C++ Programming Language, 2nd Edition.
Program developers must take care that any user-defined
new
operators always return pointers to quadword aligned memory.
4.11 Size-of-Array Argument to delete Operator
If a size-of-array argument accompanies a delete operator, Compaq C++ ignores the argument and issues a warning. The following example includes an anachronistic use of the delete operator:
int main() { int *a = new int [20]; int *b = new int [20]; delete[20] a; //old-style; argument ignored, warning issued delete[] b; return 0; } |
Do not depend on the newline character (\
n
) to flush your terminal output buffer. A previous stream
implementation might have done so, but this behavior is not in
conformance with Version 2.0 of the AT&T
iostream
library. If you want to flush the output buffer, use the
endl
manipulator or the
flush
member function.
4.13 Missing Parenthesis Error Message
Situations occur in which a simple typographical error generates a missing parenthesis error message. In the following example, the class name CaseSensitive is incorrectly specified as Casesensitive in the constructor declaration:
class CaseSensitive { void Test( const Casesensitive &foo ); }; |
As the compiler parses the argument declaration list, it first sees
const
, which it interprets as a type specifier. The compiler then sees
Casesensitive
, which it interprets as a
dname
. Among the next legal tokens are the equal sign, comma, and closing
parenthesis. Upon finding an ampersand, the compiler expects a closing
parenthesis. With all other possibilities exhausted, the compiler has
what appears to be a legal argument declaration list, after which the
closing parenthesis is the only allowable token. The compiler expected
one thing but encountered something else. Often, inserting newline
characters can isolate the offending token.
4.14 Segmentation Faults
If you encounter a segmentation fault (signal
SEGV
) while running your program, it might be because you built your binary
with the wrong linker. Use the
cxx
command, not the
ld
command, to build a binary; otherwise, the run-time startup and other
crucial code will not build properly.
4.15 Source File Extensions
Compaq C++ automatically treats files with a .c extension as C language source files and passes the files to the cc command. For Compaq C++ to compile them, your files must have one of the following extensions:
.cxx .CXX .cpp .CPP .cc .CC .C |
You also can use the
-x
option to direct the compiler to ignore file-name extensions and treat
all named files, other than those with an
.a
or
.o
extension, as C++ source files.
4.16 Incrementing Enumerations
Some other C++ implementations let you perform integer arithmetic,
including ++, on enumerated types; Compaq C++ does not allow this.
4.17 Overloading Disambiguation
Compaq C++ applies the conversions specified in The Annotated C++ Reference Manual more strictly than do some C++ implementations. This can cause Compaq C++ to interpret some calls to overloaded functions as ambiguous, even though some other C++ implementations may be able to process them. The following example shows one of these ambiguous calls to overloaded functions:
#include <iostream.hxx> enum logical { false, true }; logical false1 = false; /* const unsigned int */ unsigned int false2 = 0; const int false3 = 0; void foo(void*) { cout << "foo(void*)\\n"; } void foo(int) { cout << "foo(int)\\n"; } main() { foo(false1); // Constant expression that is not an exact match for // int. Can be converted using standard conversions // to both int and void* foo(false2); // not a constant expression foo(false3); // exact match of int } |
In this example, false1 is ambiguous. As an unsigned const int with a value of 0, it can be converted either to void* or to int .
The following example shows how to properly declare the logical:
enum logical { false = 1, true }; |
Some other C++ implementation might handle scoping differently from Compaq C++, as shown by the following code fragment:
foo(int dup) // first dup declared { // first dup used for(int dup; dup < 10; dup++) // second dup declared { // second dup used int dup; // third dup declared // third dup used } // first dup used } main() { foo(5); } |
In this example,
dup
is used three times: once in the formal parameter declaration, once in
the
for
statement declaration, and once in the
for
block. The third declaration is correct, because it is within a block.
The second declaration, should be flagged as an error because it is not
within a separate block. Compaq C++ will flag this as an error.
4.19 Guidelines for Writing Clean 64-Bit Code
Paying careful attention to data types can ensure that your code works on both 32-bit and 64-bit systems. Use the following guidelines to write clean 64-bit code:
long foo, bar; foo = 1L << bar; |
For more information, see the Tru64 UNIX Migration Guide.
Previous | Next | Contents | Index |