Chapter 3. C++ Dialect Support

This chapter describes the C++ language implemented by the new 32-bit (-n32)and 64-bit (-64) MIPSpro C++ compilers. The old 32-bit (-o32) compiler accepts an older version of the C++ language, which is not discussed in this chapter.

This chapter contains the following major sections:

About the Compiler

The C++ compiler accepts the C++ language as defined by The Annotated C++ Reference Manual (ARM) including templates, exceptions, and the anachronisms discussed in this chapter. This language is augmented by extensions from the C++ International Standard.

The -anach command line option enables anachronisms from older C++ implementations. By default, anachronisms are disabled. See “Anachronisms Accepted”, for details.

By default, the compiler accepts certain extensions to the C++ language; these extensions are flagged as warnings if you use the -ansiW option, and as errors if you use the -ansiE option. See “Extensions Accepted in Default Mode”, for details.

The compiler also has a Cfront-compatibility mode (enabled by the -cfront option), which duplicates a number of features and bugs of Cfront, an older C++ compiler that accepted output from a C preprocessor and gave input to a C compiler. Complete compatibility is not guaranteed or intended; the mode is there to allow programmers who have used Cfront features to continue to compile their existing code. By default, the compiler does not support Cfront compatibility. See “Extensions Accepted in Cfront-Compatibility Mode”, and “Cfront Compatibility Restrictions”, for details.

New Language Features

The following features, not in the ARM but in the C++ International Standard, are accepted by the MIPSpro C++ compilers.

  • 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.

  • Use of a global-scope qualifier in member references of the form x.::A::B and p->::A::B is allowed.

  • The precedence of the third operand of the ? operator is changed.

  • If control reaches the end of the main() routine, and main() has an integral return type, it is treated as if a return 0; statement were 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 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 are permitted in class definitions and class template definitions.

  • Type template parameters are permitted to have default arguments.

  • Function templates may have non-type 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.

  • Static data member declarations can be used to declare member constants.

  • wchar_t is recognized as a keyword and a distinct type.

  • bool is recognized.

  • Run-time type identification (RTTI), including dynamic_cast and the typeid operator, is implemented.

  • Declarations in tested conditions (in 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 nested classes outside of the enclosing class is allowed.

  • mutable is accepted on non-static data member declarations.

  • Namespaces are implemented, including using declarations and directives. Access declarations are broadened to match the corresponding using declarations.

  • Explicit instantiation of templates is implemented.

  • The typename keyword is recognized.

  • explicit is accepted to declare non-converting 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.

  • cv-qualifiers (cv stands for const volatile) are retained on rvalues (in particular, on function return values).

  • The distinction between trivial and non-trivial constructors has been implemented, as has the distinction between POD (point of definition) constructs, such as, C-style structs, 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 may be used in an explicit destructor call.

  • Placement delete is implemented.

  • An array allocated via a placement new can be deallocated via delete.

  • enum types are considered to be non-integral types.

  • Partial specialization of class templates is implemented.

Non-implemented Language Features

The following features which are in the C++ International Standard but are not in the ARM, are not accepted by the MIPSpro C++ compilers:

  • extern inline functions are not supported.

  • Digraphs are not recognized.

  • Operator keywords (for example, and and bitand) are not recognized.

  • It is not possible to overload operators using functions that take enum types and no class types.

  • The new lookup rules for member references of the form x.A::B and p->A::B are not yet implemented.

  • enum types cannot contain values larger than can be contained in an int.

  • reinterpret_cast does not allow casting a pointer to member of one class to a pointer to a member of another class if the classes are unrelated.

  • Name binding in templates in the style of N0288/93-0081 is not implemented.

  • In a reference of the form f()->g(), with g() a static member function, f() is not evaluated. This is as required by the ARM, however, the C++ International Standard requires that f() be evaluated.

  • Class name injection is not implemented.

  • Putting a try and catch around the initializers and body of a constructor is not implemented.

  • The notation :: template (and ->template, and so forth) is not implemented.

Anachronisms Accepted

The following anachronisms are accepted when anachronisms are enabled (via the -anach option):

  • overload is allowed in function declarations. It is accepted and ignored.

  • Definitions are not required for static data members that can be initialized using 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 may 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 may be omitted in a base class initializer if there is only one immediate base class.

  • 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 may participate in function overloading as though it were prototyped. Default argument promotion is not applied to parameter types of such functions when the check for compatibility is performed, so that the following declares the overloading of two functions named f():

    int f(int);
    int f(x) char x; { return x; } 


    Note: In C, this code is legal but has a different meaning: a tentative declaration of f() is followed by its definition.


  • A reference to a non -const class can be bound to a class rvalue of the same type or a derived type thereof.

    struct A {
        A(int);
        A operator=(A&);
        A operator+(const A&);
    };
    main () {
        A b(1);
        b = A(1) + A(2); // Allowed as anachronism
    }

Extensions Accepted in Default Mode

The following extensions are accepted by default (they can be flagged as errors or warnings by using -ansiE or -ansiW):

  • A friend declaration for a class may omit the class keyword, as in the following:

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

  • Constants of scalar type may be defined within classes, as in the following:

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

  • In the declaration of a class member, a qualified name may be used, as in the following:

    struct A {
        int A::f(); // Should be int f();
    };

  • The preprocessing symbol c_plusplus is defined in addition to the standard __cplusplus.

  • 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.) Here's an example:

    struct A { };
    struct B : public A {
    B& operator=(A&);
    };

    By default, as well as in Cfront-compatibility mode, there is 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.

Extensions Accepted in Cfront-Compatibility Mode

The following extensions are accepted in Cfront-compatibility mode (via the -cfront option):

  • Type qualifiers on the this parameter may to be dropped in contexts such as the following example:

    struct A {
        void f() const;
    };
    void (A::*fp)() = &A::f;

    This is actually a safe operation. A pointer to a const function may 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 actually does not modify the object. The opposite assignment would not be safe.

  • Conversion operators specifying conversion to void are allowed.

  • A non-standard friend declaration may introduce a new type. A friend declaration that omits the elaborated type specifier is allowed in default mode, but in Cfront-compatibility mode, the declaration is also allowed to introduce a new type name.

    struct A {
        friend B;
    };

  • The third operator of the ? operator is a conditional expression instead of an assignment expression as it is in the current C++ International Standard.

  • 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 may be initialized with a 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 value zero is not considered to be a null pointer constant.

  • An alternate form of declaring pointer-to-member-function variables is supported. Consider the following code sample:

    struct A {
        void f(int);
        static void f(int);
        typedef void A::T3(int);  // Non-std typedef declaration
        typedef void T2(int);     // Std typedef declaration
    };
    typedef void A::T(int);  // Non-std typedef declaration
    T* pmf = &A::f;          // Non-std ptr-to-member declaration
    A::T2* pf = A::sf;       // Std ptr to static mem declaration
    A::T3* pmf2 = &A::f;     // Non-std ptr-to-member declaration

    where T is construed to name a routine type for a non-static member function of class A that takes an int argument and returns void; the use of such types is restricted to non-standard pointer-to-member declarations. The declarations of T and pmf in combination are equivalent to a single standard pointer-to-member declaration, such as in the following example:

    void (A::* pmf)(int) = &A::f;

    A non-standard pointer-to-member declaration that appears outside a class declaration, such as the declaration of T, is normally invalid 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. Version 2.1 of Cfront accepts declarations, such as T, even when A is an incomplete type; so this case is also excepted.

  • 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-compatibility mode
        int D::* pmi2 = &D::i;  // OK
    }


    Note: Protected member access checking for other operations (in other words, everything except taking a pointer-to-member address) is done in the normal manner.


  • The destructor of a derived class may implicitly call the private destructor of a base class. In default mode this is an error, but in Cfront-compatibility mode it is reduced to a warning. For example:

    class A {
        ~A();
    };
    class B : public A {
        ~B();
    };
    B::~B(){}    // Error except in Cfront-compatibility 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 x is a function; but in Cfront-compatibility mode int(d is an argument and x is a variable.

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

    Similarly, the statement

    int xyz(int());

    declares a function named xyz, that takes a parameter of type "function taking no arguments and returning an int." In Cfront-compatibility mode, this is interpreted as a declaration of an object that is initialized with the value int() (which evaluates to zero).

  • A named bit-field may have a size of zero. The declaration is treated as though no name had been declared.

  • Plain bit fields (in other words, bit fields declared with type int) are always unsigned.

  • The name given in an elaborated type specifier is permitted to be a typedef name that is the synonym for a class name, for example:

    typedef class A T;
    class T *pa;               // Not an error in cfront-compatibility mode

  • No warning is issued on duplicate size and sign specifiers.

    short short int i;  // No warning given in cfront-compatibility 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, as in the following example:

    f(1, 2, );

  • A constant pointer-to-member function may be cast to a pointer to function. 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 (in other words, like C structures), and the destructor is not called on the new 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.


    Note: Because the argument is passed differently (by value instead of by address), code like this compiled in Cfront-compatibility 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-compatibility mode

Cfront Compatibility Restrictions

Even when you specify the -cfront option, the N32, 64, and O32 C++ compilers are not completely backwards-compatible with Cfront. The N32, 64, and O32 compilers reject the following source constructs that Cfront ignores:

  • Assignment to this in constructors and destructors is not allowed (O32 generates a warning.)

  • If a C++ comment line (//) is terminated with a backslash, the MIPSpro compilers (correctly) continue the comment line into the next source line. Cfront uses the standard UNIX cpp and terminates the comment at the end of the line.

  • You must have an explicit declaration of a constructor or destructor in the class if there is an explicit definition of it outside the class.

  • You may not pass a pointer to volatile data to a function that is expecting a pointer to non-volatile data.

  • The MIPSpro compilers do not disambiguate between overloaded functions with a char* and long parameter, respectively, when called with an expression that is a zero cast to a char type.

  • You may not use redundant type specifiers.

  • When in a conditional expression, the MIPSpro compilers do not convert a pointer to a class to an accessible base class of that class.

  • You may not assign a comma-expression ending in a literal constant expression "0" to a pointer; the "0" is treated as an int.

  • The MIPSpro compilers mangle member functions declared as extern "C" differently from Cfront. The CC command does not strip the type signature when you are building the mangled name. If you try to do so, the following warning is issued:

    Mangling of classes within an extern "C" block does not
    match cfront name mangling.

    You may not be able to link code containing a call to such a function with code containing the definition of the function that was compiled with Cfront.