Cray C and C++ Dialect Use

This topic details the features of the C and C++ languages that are accepted by the Cray C and C++ compilers, including certain language dialects and anachronisms.

This topic details the features of the C and C++ languages that are accepted by the Cray C and C++ compilers, including certain language dialects and anachronisms. Users should be aware of these details, especially users who are porting codes from other environments.

Cray C++ Language Conformance

The Cray C compiler accepts the C++ language as defined by the ISO/IEC 14882:2003 standard, except for exported templates. C++ supports the ISO 2003 Standard Template Library (STL) headers but abrogates support for pre-standard template headers that have the .h extension.

C++ codes that use the pre-standard template headers must be updated to the ISO C++ standard.

The Cray C++ compiler also has a cfront compatibility mode, which duplicates a number of features and bugs of cfront. Complete compatibility is not guaranteed or intended. The mode allows programmers who have used cfront features to continue to compile their existing code. Command line options are also available to enable and disable anachronisms and strict standard-conformance checking.

Cray C++ Features

The following features, which are in the ISO/IEC 14882:2003 standard but not in traditional C++[1], are supported:
  • The dependent statement of an if, while, do-while, or for is considered to be a scope, and the restriction on having such a dependent statement be a declaration is removed.
  • The expression tested in an if, while, do-while, or for, as the first operand of a ? operator, or as an operand of the &&, ||, or ! operators may have a pointer-to-member type or a class type that can be converted to a pointer-to-member type in addition to the scalar cases permitted by the ARM.
  • Qualified names are allowed in elaborated type specifiers.
  • A global-scope qualifier is allowed in member references of the form x.::A::B and p->::A::B.
  • The precedence of the third operand of the ? operator is changed.
  • If control reaches the end of the main() routine, and the main() routine has an integral return type, it is treated as if a return 0; statement was executed.
  • Pointers to arrays with unknown bounds as parameter types are diagnosed as errors.
  • A functional-notation cast of the form A() can be used even if A is a class without a (nontrivial) constructor.
  • The temporary that is created gets the same default initialization to zero as a static object of the class type.
  • A cast can be used to select one out of a set of overloaded functions when taking the address of a function.
  • Template friend declarations and definitions are permitted in class definitions and class template definitions.
  • Type template parameters are permitted to have default arguments.
  • Function templates may have nontype template parameters.
  • A reference to const volatile cannot be bound to an rvalue.
  • Qualification conversions such as conversion from T** to T const * const are allowed.
  • Digraphs are recognized.
  • Operator keywords (for example, and or bitand) are recognized.
  • Static data member declarations can be used to declare member constants.
  • bool is recognized.
  • RTTI (run time type identification), including dynamic_cast and the typeid operator, is implemented.
  • Declarations in tested conditions (within if, switch, for, and while statements) are supported.
  • Array new and delete are implemented.
  • New-style casts (static_cast, reinterpret_cast, and const_cast) are implemented.
  • Definition of a nested class outside its enclosing class is allowed.
  • mutable is accepted on nonstatic data member declarations.
  • Namespaces are implemented, including using declarations and directives.
  • Access declarations are broadened to match the corresponding using declarations.
  • The typename keyword is recognized.
  • explicit is accepted to declare nonconverting constructors.
  • The scope of a variable declared in the for-init-statement of a for loop is the scope of the loop (not the surrounding scope).
  • Member templates are implemented.
  • The new specialization syntax (using template <>) is implemented.
  • Type qualifiers, const and volatile referred to as cv-qualifiers are retained on rvalues (in particular, on function return values).
  • The distinction between trivial and nontrivial constructors has been implemented, as has the distinction between process overlay directives (PODs) and non-PODs with trivial constructors.
  • The linkage specification is treated as part of the function type (affecting function overloading and implicit conversions).
  • A typedef name can be used in an explicit destructor call.
  • Placement delete is supported.
  • An array allocated via a placement new can be deallocated via delete.
  • enum types are considered to be nonintegral types.
  • Partial specification of class templates is implemented.
  • Partial ordering of function templates is implemented.
  • Function declarations that match a function template are regarded as independent functions, not as “guiding declarations” that are instances of the template.
  • It is possible to overload operators using functions that take enum types and no class types.
  • Explicit specification of function template arguments is supported.
  • Unnamed template parameters are supported.
  • The new lookup rules for member references of the form x.A::B and p->A::B are supported.
  • The notation :: template (and –>template, etc.) is supported.
  • In a reference of the form f()->g(), with g a static member function, f() is evaluated. Likewise for a similar reference to a static data member. The ARM specifies that the left operand is not evaluated in such cases.
  • enum types can contain values larger than can be contained in an int.
  • Default arguments of function templates and member functions of class templates are instantiated only when the default argument is used in a call.
  • String literals and wide string literals have const type.
  • Argument-dependent (Koenig) lookup of function names is implemented.
  • Class and function names declared only in unqualified friend declarations are not visible except for functions found by argument-dependent lookup.
  • A void expression can be specified on a return statement in a void function.
  • reinterpret_cast allows casting a pointer to a member of one class to a pointer to a member of another class even when the classes are unrelated.
  • Two-phase name binding in templates as described in the Working Paper is implemented.
  • Putting a try/catch around the initializers and body of a constructor is implemented.
  • template parameters are implemented.
  • Universal character set escapes (e.g., \uabcd) are implemented.
  • extern inline functions are supported.
  • Covariant return types on overriding virtual functions are supported.

C++ Libraries

Most standard C++ features are supported, except the following:
  • String classes using basic string class templates with wide character types or that use the wstring standard template class.
  • I/O streams using wide character objects.
  • File-based streams using file streams with wide character types (wfilebuf, wifstream, wofstream, and wfstream)
  • Multiple localization libraries; Cray C++ supports only one locale
The C++ standard provides a standard naming convention for library routines. Therefore, classes or routines that use wide characters are named appropriately. For example, the fscanf and sprintf functions do not use wide characters, but the fwscanf and swprintf function do.

C++ Anachronisms Accepted

C++ anachronisms are enabled by using the -h anachronisms command line option. When anachronisms are enabled, the following anachronisms are accepted:
  • overload is allowed in function declarations. It is accepted and ignored.
  • Definitions are not required for static data members that can be initialized by using the default initialization. The anachronism does not apply to static data members of template classes; they must always be defined.
  • The number of elements in an array can be specified in an array delete operation. The value is ignored.
  • A single operator++() and operator--() function can be used to overload both prefix and postfix operations.
  • The base class name can be omitted in a base class initializer if there is only one immediate base class.
  • Assignment to the this pointer in constructors and destructors is allowed. This is only allowed if anachronisms are enabled and the assignment to this configuration parameter is enabled.
  • A bound function pointer (a pointer to a member function for a given object) can be cast to a pointer to a function.
  • A nested class name may be used as a non-nested class name if no other class of that name has been declared. The anachronism is not applied to template classes.
  • A reference to a non-const type may be initialized from a value of a different type. A temporary is created, it is initialized from the (converted) initial value, and the reference is set to the temporary.
  • A reference to a non-const class type may be initialized from an rvalue of the class type or a derived class thereof. No (additional) temporary is used.
  • A function with old-style parameter declarations is allowed and can participate in function overloading as though it were prototyped. Default argument promotion is not applied to parameter types of such functions when checking for compatibility, therefore, the following statements declare the overloading of two functions named f:
    int f(int); 
    int f(x) char x; { return x; }

Extensions Accepted in Normal C++ Mode

Certain C++ extensions are accepted except when strict standard conformance mode is enabled, in which case a warning or caution message may be issued.

A friend declaration for a class can omit the class keyword


class B;
class A  {
   friend B;   // Should be "friend class B"
};

Constants of scalar type can be defined within classes

class A {
    const int size=10;
    int a[size];
};

Default assignment operator

An assignment operator declared in a derived class with a parameter type matching one of its base classes is treated as a “default” assignment operator; that is, such a declaration blocks the implicit generation of a copy assignment operator. This is cfront behavior that is known to be relied upon in at least one widely used library.
struct A { };
struct B : public A {
   B& operator=(A&);
};

By default, as well as in cfront compatibility mode, there will be no implicit declaration of B::operator=(const B&), whereas in strict-ANSI mode, B::operator=(A&) is not a copy assignment operator and B::operator=(const B&) is implicitly declared.

Implicit type conversion between a pointer to an extern "C" function and a pointer to an extern "C++" function


extern "C" void f(); // f's type has extern "C" linkage
void (*pf)()         // pf points to an extern "C++" function
             = &f;   // error unless implicit conversion allowed

The ? operator

The ? operator, for which the second and third operands are string literals or wide string literals, can be implicitly converted to one of the following:
char *
wchar_t *
In C++ string literals are const. There is a deprecated implicit conversion that allows conversion of a string literal to char *, dropping the const. That conversion, however, applies only to simple string literals. Allowing it for the result of a ? operation is an extension:
char *p = x ? "abc" : "def";

Extensions Accepted in C or C++ Mode

The following extensions are accepted in C or C++ mode except when strict standard conformance modes is enabled, in which case a warning or caution message may be issued.
  • The special lint comments /*ARGSUSED*/, /*VARARGS*/ (with or without a count of nonvarying arguments), and /*NOTREACHED*/ are recognized.
  • A translation unit (input file) can contain no declarations.
  • Comment text can appear at the ends of preprocessing directives.
  • Bit fields can have base types that are enum or integral types in addition to int and unsigned int. This corresponds to the ANSI Common Extensions topic.
  • enum tags can be incomplete as long as the tag name is defined and resolved by specifying the brace-enclosed list later.
  • An extra comma is allowed at the end of an enum list.
  • The final semicolon preceding the closing of a struct or union type specifier can be omitted.
  • A label definition can be immediately followed by a right brace ( } ). (Normally, a statement must follow a label definition.)
  • An empty declaration (a semicolon preceded by nothing) is allowed.
  • An initializer expression that is a single value and is used to initialize an entire static array, struct, or union does not need to be enclosed in braces. ANSI C requires braces.
  • In an initializer, a pointer constant value can be cast to an integral type if the integral type is large enough to contain it.
  • The address of a variable with register storage class may be taken.
  • In an integral constant expression, an integer constant can be cast to a pointer type and then back to an integral type.
  • In duplicate size and sign specifiers (for example, short short or unsigned unsigned) the redundancy is ignored.
  • Benign redeclarations of typedef names are allowed. That is, a typedef name can be redeclared in the same scope with the same type.
  • Dollar sign ($) characters can be accepted in identifiers by using the -h calchars command line option. This is not allowed by default.
  • Numbers are scanned according to the syntax for numbers rather than the pp-number syntax. Thus, 0x123e+1 is scanned as three tokens instead of one token that is not valid. If the -h conform option is specified, the pp-number syntax is used.
  • Assignment and pointer differences are allowed between pointers to types that are interchangeable but not identical, for example, unsigned char * and char *. This includes pointers to integral types of the same size (for example, int * and long *). Assignment of a string constant to a pointer to any kind of character is allowed without a warning.
  • Assignment of pointer types is allowed in cases where the destination type has added type qualifiers that are not at the top level (for example, int ** to const int **). Comparisons and pointer difference of such pairs of pointer types are also allowed.
  • In operations on pointers, a pointer to void is always implicitly converted to another type if necessary, and a null pointer constant is always implicitly converted to a null pointer of the right type if necessary. In ANSI C, these are allowed by some operators, and not by others (generally, where it does not make sense).
  • Pointers to different function types may be assigned or compared for equality (==) or inequality (!=) without an explicit type cast. This extension is not allowed in C++ mode.
  • A pointer to void can be implicitly converted to or from a pointer to a function type.
  • External entities declared in other scopes are visible:
    void f1(void) { extern void f(); }
    void f2() { f(); /* Using out of scope declaration */ }
  • In C mode, end-of-line comments (//) are supported.
  • A non-lvalue array expression is converted to a pointer to the first element of the array when it is subscripted or similarly used.
  • The fortran keyword. For more information, see fortran Keyword.
  • Cray hexadecimal floating point constants. For more information, see Hexadecimal Floating-point Constants.

C++ Extensions Accepted in cfront Compatibility Mode

The cfront compatibility mode is enabled by the -h cfront command-line option. The following extensions are accepted in cfront compatibility mode:
  • Type qualifiers on the this parameter are dropped in contexts such as in the following example:
    struct A {
       void f() const;
    };
    void (A::*fp)() = &A::f;

    This is a safe operation. A pointer to a const function can be put into a pointer to non-const, because a call using the pointer is permitted to modify the object and the function pointed to will not modify the object. The opposite assignment would not be safe.

  • Conversion operators that specify a conversion to void are allowed.
  • A nonstandard friend declaration can introduce a new type. A friend declaration that omits the elaborated type specifier is allowed in default mode, however, in cfront mode the declaration can also introduce a new type name. An example follows:
    struct A {
       friend B;
    };
  • The third operator of the ? operator is a conditional expression instead of an assignment expression.
  • A reference to a pointer type may be initialized from a pointer value without use of a temporary even when the reference pointer type has additional type qualifiers above those present in the pointer value. For example:
    int *p;
    const int *&r = p;   // No temporary used
  • A reference can be initialized to NULL.
  • Because cfront does not check the accessibility of types, access errors for types are issued as warnings instead of errors.
  • When matching arguments of an overloaded function, a const variable with a value of 0 is not considered to be a null pointer constant. In general, in overload resolution, a null pointer constant must be entered as "0” to be considered a null pointer constant (e.g., '\0' is not considered a null pointer constant).
  • An alternate form of declaring pointer-to-member-function variables is supported, as shown in the following example:
    struct A {
     void f(int);
     static void sf(int);
     typedef void A::T3(int);  // nonstd typedef decl
     typedef void T2(int);     // std typedef
    };
    typedef void A::T(int);    // nonstd typedef decl
    T* pmf = &A::f;            // nonstd ptr-to-member decl
    A::T2* pf = A::sf;         // std ptr to static mem decl
    A::T3* pmf2 = &A::f;       // nonstd ptr-to-member decl
    In this example, T is construed to name a function type for a nonstatic member function of class A that takes an int argument and returns void; the use of such types is restricted to nonstandard pointer-to-member declarations. The declarations of T and pmf in combination are equivalent to the following single standard pointer-to-member declaration:
    void (A::* pmf)(int) = &A::f;

    A nonstandard pointer-to-member declaration that appears outside of a class declaration, such as the declaration of T, is normally not valid and would cause an error to be issued. However, for declarations that appear within a class declaration, such as A::T3, this feature changes the meaning of a valid declaration. cfront version 2.1 accepts declarations, such as T, even when A is an incomplete type; so this case is also accepted.

  • Protected member access checking is not done when the address of a protected member is taken. For example:
  • class B { protected: int i; };
    class D : public B { void mf()};
    
    void D::mf() {
     int B::* pmi1 = &B::i; // error, OK in cfront mode
     int D::* pmi2 = &D::i; // OK
    }

    Protected member access checking for other operations (such as everything except taking a pointer-to-member address) is done normally

  • The destructor of a derived class can implicitly call the private destructor of a base class. In default mode, this is an error but in cfront mode it is reduced to a warning. For example:
    
    class A {
       ~A();
    };
    class B : public A {
       ~B();
    };
    B::~B(){}           // Error except in cfront mode
  • When disambiguation requires deciding whether something is a parameter declaration or an argument expression, the pattern type-name-or-keyword (identifier ...) is treated as an argument. For example:
    class A { A(); };
    double d;
    A x(int(d));
    A(x2);

    By default, int(d) is interpreted as a parameter declaration (with redundant parentheses), and so x is a function; but in cfront compatibility mode int(d) is an argument and x is a variable.

    The declaration A(x2) is also misinterpreted by cfront. It should be interpreted as the declaration of an object named x2, but in cfront mode it is interpreted as a function style cast of x2 to the type A.

    Similarly, the following declaration declares a function named xzy, that takes a parameter of type function taking no arguments and returning an int. In cfront mode, this is interpreted as a declaration of an object that is initialized with the value int(), which evaluates to 0.
    int xyz(int());
  • A named bit field can have a size of 0. The declaration is treated as though no name had been declared.
  • Plain bit fields (such as bit fields declared with a type of int) are always signed.
  • The name given in an elaborated type specifier can be a typedef name that is the synonym for a class name. For example:
    typedef class A T;
    class T *pa;                   // No error in cfront mode
  • No warning is issued on duplicate size and sign specifiers, as shown in the following example:
    short short int i;  // No warning in cfront mode
  • Virtual function table pointer-update code is not generated in destructors for base classes of classes without virtual functions, even if the base class virtual functions might be overridden in a further derived class. For example:
    struct A {
      virtual void f() {}
      A() {}
      ~A() {}
    };
    struct B : public A {
      B() {}
      ~B() {f();} // Should call A::f according to ARM 12.7
    };
    struct C : public B {
      void f() {}
    } c;

    In cfront compatibility mode, B::~B calls C::f.

  • An extra comma is allowed after the last argument in an argument list. For example:
    f(1, 2, );
  • A constant pointer-to-member function can be cast to a pointer-to-function, as in the following example. A warning is issued.
    struct A {int f();};
    main () {
    int (*p)();
    p = (int (*)())A::f;                     // Okay, with warning
    }
  • Arguments of class types that allow bitwise copy construction but also have destructors are passed by value like C structures, and the destructor is not called on the copy. In normal mode, the class object is copied into a temporary, the address of the temporary is passed as the argument, and the destructor is called on the temporary after the call returns. Because the argument is passed by value instead of by address, code like this compiled in cfront mode is not calling-sequence compatible with the same code compiled in normal mode. In practice, this is not much of a problem, since classes that allow bitwise copying usually do not have destructors.
  • A union member may be declared to have the type of a class for which the user has defined an assignment operator (as long as the class has no constructor or destructor). A warning is issued.
  • When an unnamed class appears in a typedef declaration, the typedef name may appear as the class name in an elaborated type specifier. For example:
    typedef struct { int i, j; } S;
    struct S x;                         // No error in cfront mode
  • Two member functions may be declared with the same parameter types when one is static and the other is nonstatic with a function qualifier. For example:
    class A {
      void f(int) const;
      static void f(int); // No error in cfront mode
    };
  • The scope of a variable declared in the for-init-statement is the scope to which the for statement belongs. For example:
    int f(int i) {
      for (int j = 0; j < i; ++j) { /* ... */ }
      return j; // No error in cfront mode
    }
  • Function types differing only in that one is declared extern "C" and the other extern "C++" can be treated as identical:
    typedef void (*PF)();
    extern "C" typedef void (*PCF)();
    void f(PF);
    void f(PCF);

    By contrast, in standard C++, PF and PCF are different and incompatible types; PF is a pointer to an extern "C++" function whereas PCF is a pointer to an extern "C" function; and the two declarations of f create an overload set.

  • Functions declared inline have internal linkage.
  • enum types are regarded as integral types.
  • An uninitialized const object of non-POD class type is allowed even if its default constructor is implicitly declared as in the following example:
    struct A { virtual void f(); int i; };
    const A a;
  • A function parameter type is allowed to involve a pointer or reference to array of unknown bounds.
  • If the user declares an operator= function in a class, but not one that can serve as the default operator=, and bitwise assignment could be done on the class, a default operator= is not generated. Only the user-written operator= functions are considered for assignments, so bitwise assignment is not done.