New C++ Features



Table of Contents      

Introduction

Getting Up and Running

 

Debugging

New C++ Features

 

External Interface   

Tutorials

Man Page

FAQ   


This chapter describes KAI C++ features that are fairly recent additions to the language. It is intended as an introduction to these features, and not an exhaustive tutorial. These features are described in more detail by the ISO C++ standard.

For more information on the standards process, see the comp.std.c++ FAQ.

Occasionally, the new features conflict with old features used by existing codes. See our migration notes on how to deal with these conflicts.

9.1 Namespaces

Namespaces are implemented. The new keywords are namespace and using.

9.1.1 Compiling Pre-Namespace Codes

Some user-visible run-time routines are in the namespace std, as specified in the ISO Standard. The run-time routines in these namespaces are declared in the following four header files:

To allow existing code without namespaces to use the run-time routines, there are four corresponding header files with .h suffixes.

Each of these headers simply includes the corresponding ISO header, and then imports the defined symbols from std into the global namespace, via using declarations. If your code does not contain namespaces, but includes these headers files, you should be able to compile it without difficulty.

The KAI C++ header files use the following symbols that are predefined by the KAI C++ compiler:

These are for internal use by KAI C++.

9.1.2 Argument Dependent Name Lookup (Koenig) Lookup

KAI C++ 3.4 implements argument dependent name lookup, also known colloquially as Koenig lookup. This means that the compiler encounters a function call, and the function identifier is unqualified, the compiler will look not only in the usual namespaces for the identifier, but also in namespaces associated with the argument types. Below is an example:
    namespace Farm {
	struct Cow {};
	void Milk(Cow&);
    }
     
    namespace City {
	void f( Farm::Cow& c) {
	    Milk(c);
	}
    }
The example requires argument dependent name lookup to compile the call site Milk(c), since Milk is defined in a namespace that does not enclose the call site,

.2 New Features for Templates

KAI C++ supports the advanced template features listed below.

9.2.1 Template Friend Declarations

Template friend declarations and definitions are permitted in class definitions and class template definitions. Below is an example of a class definition that declares a template class Peer<T> as a friend.

    class Person {
    private:
        float my_money;
        template<class T> friend class Peer;
    };

9.2.2 Default Arguments for Type Templates

Default arguments for type templates are permitted. For example, the following code is allowed:

    template<class A, class B=double> 
    class Pair {
    public:
        A a;
        B b;
    };

    Pair<char,int> pci;
    Pair<char> pc;        // B defaults to double.

9.2.3 Non-Type Template Parameters for Function Templates

Non-type template parameters for function templates are permitted. For example, the template below returns the number of columns in a two-dimensional array:

    template<class T,int N> 
    columns( T a[][N] ) {
        return N;
    }

The following fragment shows how it is used.

    double y[30][40];
    cout << columns(y);

The template parameters must be used within the declaration of the function's parameter type. Because the leading dimension of an array is not part of the type of an array, the following is not legal:

    // ILLEGAL
    template<class T,int M> 
    rows( T a[M][] ){
        return;
    }

9.2.4 Explicit Instantiation of Templates

Templates may be explicitly instantiated with the Standard syntax. For instance, consider these templates:

    template<class T>
    class Foo {           // Template class 
    public:
        template<class S>
        void 
        noop( S& );       // Member template declaration
    };

    template<class T>     // Member template definition
    template<class S>
    void 
    Foo<T>::noop( S& ) {}

    template<class T>
    void 
    Bar( T  x ) {}        // Template function

Below are some sample explicit instantiations.

    template class Foo<int>;                    // #1
    template void Foo<char>::noop( short& );    // #2
    template void Bar( float );                 // #3

Line #1 forces instantiation of class Foo<int>. All non-template non-inline member functions and static data members of class Foo<int> will be instantiated. Line #2 forces instantiation of member Foo<char>::noop(short&). Only the member is instantiated. Line #3 forces instantiation of function Bar(float).

The general syntax for explicit template instantiation is simply:

    template declaration

where declaration is a non-template declaration.

9.2.5 Keyword typename

The keyword typename is now recognized inside templates. The keyword is used inside templates to declare types that are not template parameters nor declared in the enclosing scope. The keyword as added by the ISO committee so that implementations can do some parsing and type checking of template definitions without instantiating them.

The following example demonstrates the sort of problem that the keyword typename solves.

    template<class T> class Problem {
    public:
        void ack() {
            typename T::A * B;
        }
    };

Without the keyword typename, the compiler would not know whether the member function is declaring B of type T::A*, or multiplying A and B. The ISO rules require the use of typename even in some spots where it might appear "obvious" that the identifier is a type name. In particular, it is usually necessary when using a qualified name in a typedef.

    template<class T> class Machine {
    public:
        typedef typename T::A gear_type;
    };

The identifier following typename must be a qualified identifier.

Keywords typename template for member templates

There is a further complication if the qualified name is a member template identifier. If so, the ISO Standard (but not the December 1996 draft) requires that the keyword template preceed the qualified template name.

    namespace std {                 // Excerpted from <memory>
	template<class T>
	struct allocator {
	    template<class U> struct rebind { typedef allocator<U>  other; };
	};
    }

    template<class Allocator>       
    struct Decanter {
	typename Allocator::template rebind<int>::other int_allocator;
	...
    };

In strict mode, KAI C++ 3.3 and later insist that the keyword template appear as shown above in the declaration of int_allocator. If it is omitted, the ISO rules require that KAI C++ interpret the member as a non-template member, and KAI C++ will complain:

    template parameter "Allocator::rebind" may not have a template argument list

To summarize, here is the ISO grammar in Section 7.1.5.3 of the Standarfd for an elaborated type specifier:

    elaborated-type-specifier:
	    class-key ::opt nested-name-specifieropt identifier
	    enum ::opt nested-name-specifieropt identifier 
	    typename ::opt nested-name-specifier
	    typename ::opt nested-name-specifieropt templateopt template-id


9.2.6 Member Templates

Both non-template and template classes may have template members. Below is an example declaration of a class with a template member for assignment.

    // Class for N-element vector of type T.
    template<class T,size_t N> class Vec {
    public:

        // Template member declaration for assignment of conformable vectors.
        template<class U> Vec& operator=( const Vec<U,N>& rhs );

        T& operator[]( size_t i ) {
            assert( i<N );
            return rep[i];
        }

        // Example of template friend.
        template<class U,size_t M> friend class Vec<U,M>;

    private:
        T rep[N];
    };

The template member for operator= allows conformable vectors to be assigned as long as elementwise conversion is allowed. For example, given:

    Vec<int,3> u;
    Vec<float,3> v;

the assignment

    v = u

will have the expected behavior.

If a template member of a template class is defined outside the class, there should be two ``template'' prefixes before the definition. Below is the definition for the operator= of the example.

    // Definition for assignment.
    template<class T,size_t N>
    template<class U>
    inline Vec<T,N>& Vec<T,N>::operator=( const Vec<U,N>& rhs ) {
        for( size_t i=0; i<N; i++ )
            rep[i] = rhs.rep[i];
        return *this;
    }

The outer prefixes is for the template class; the inner prefix is for the template member. It sometimes helps to think of these as analogous to the "for all" (upside-down A) quantifiers in mathematics.

Template members allow member functions, member types, and friends to all be templates. The example above shows a template friend. Notice that the template friend grants friendship wider than necessary, because it grants friendship between non-conformable vectors. There was no way around this limitation until KAI C++ 3.3, which implements explicit specification of template arguments. The section on guiding declarations discusses the friend declaration issue in more detail.

An important point to remember when writing member templates is that the compiler never uses them to generate an implicit copy-constructor or assignment operator. Thus if a class requires a user-defined copy-constructor or assignment operator, the class should define them as non-template members, even if there is also a template member that looks like it would stand in for the implicitly generated operator. For an example of a class that was written this way, take a look at the definition of class auto_ptr in KAI C++'s header file <memory>.

However, template members are used during overload resolution, even when competing against implicitly generated operators. Thus a template member is used to copy and object if it provides a better match than the implicitly generated copy constructor. Below is an example that demonstrates this principle.

    class Base {
    public:
        Base() {}
        template<class T> Base( T&);    // Member constructor	
    };

    class Derived: public Base {
    };

    Base b1;
    Derived d1;
    Base b2( b1 );  // uses implicitly generated constructor Base( const Base&)    
    Base b3( d1 );  // uses template member constructor Base( const Derived&).


9.2.8 ISO Syntax for Full Specialization

KAI C++ supports the ISO syntax for full specialization, which is really just a special case of the ISO syntax for partial specialization. Except in --strict mode, KAI C++ also allows the old Cfront syntax. A full specialization of a primary class template is an alternative definition for the class for certain values of template arguments.

For example, here is a definition for a template class with a method for copying blocks of data.

    template<class T> class block_util {
    public:
        static void copy( T * dest, const T * src, int n ) {
            for( int i=0; i<n; i++ )
        	dest[i] = src[i];
        }
    };

This completely general template is called the primary class template. For character data, it is often more efficient to use the standard library routine memcpy, because memcpy often employs obscure architecture-dependent tricks. To make the compiler call memcpy for characters, define a full specialization of block_util<char> as shown below.

    template<> class block_util<char> {
    public:
        static void copy( char * dest, char * src, int n ) {
            memcpy( dest, src, n );
        }
    };

The old Cfront syntax is similar, except that the prefix template<> would be missing.

9.2.9 Partial Specialization of Class Templates

Beginning with release 3.2, KAI C++ supports partial specialization of class templates. A partial specialization of a primary class template is an alternative definition for the class when the template arguments match the partial declaration of the template arguments in the partial specialization.

For an example, consider the class template block_util from the previous section. It had a full specialization for the special case of copying characters. It might also help to have a special case for copying pointers to any type U. Below is a partial specialization for class block_util.

    template<class U> 
    class block_util<U*> {
    public:
        static void copy( U ** dest, U ** src, int n ) {
            memcpy( dest, src, n*sizeof(U*) );
        }
    };

The syntax for a partial specialization looks quite similar to that for primary class templates. The syntax that distinguishes it from the primary class template is the <U*> after the class name. The declaration tells the compiler that for all types U, here is the definition to use for block_util<U*>.

The template for a partial specialization can have fewer or more template parameters than the primary template. Here's an example from the ISO standard (Section 14.5.4 paragraph 4):

    template<class T1, class T2, int I> class A             { }; // #1
    template<class T, int I>            class A<T, T*, I>   { }; // #2
    template<class T1, class T2, int I> class A<T1*, T2, I> { }; // #3
    template<class T>                   class A<int, T*, 5> { }; // #4
    template<class T1, class T2, int I> class A<T1, T2*, I> { }; // #5

Template #1 is the primary template. The other templates are partial specializations of it.

Here's an example of a primary template and specialization with more template parameters than the primary template.

    template<class T> class Rank {	
    public:
        static const int value = 0;	
    };

    template<class T,int N> class Rank<T[N]> {
    public:
        static const int value = 1+Rank<T>::value;
    };

This template demonstrates the power of partial specialization to perform a non-trivial computations on types. The template computes the number of dimensions of a type. For example, the following fragment prints 0 1 2.

    typedef float a;
    typedef float b[10];
    typedef float c[20][30];
    printf( "%d %d %d\n", Rank<a>::value, Rank<b>::value, Rank<c>::value );

The rank computation here is on types. A similar rank computation on objects is shown in the next section.

Declarations of partial specializations do not have default arguments. The default arguments are taken from the primary template.

9.2.10 Partial Ordering of Function Templates

KAI C++ 3.2 and later support partial ordering of function templates. This is similar to partial specialization of class templates, except that there are no primary templates. That is why despite the similarity, the feature is not called partial specialization of function templates.

Here's an example adapted from the Standard (Section 14.5.5.2 paragraph 5):

    template<class T> void f( T );           // #1
    template<class T> void f( T* );          // #2
    template<class T> void f( const T* );    // #3
    
    void bar( const int * p ) {
        f(p);                          // Calls #3
    }

The call f(p) calls an instance of template #3 since (const T*) is more specialized than the other two declarations.

Below is template function that computes the rank of an array. It returns the number of dimensions for the type.

    template<class T> 
    inline 
    int 
    Rank( T arg ) {   // Function for computing rank of array.
        return 0;     // Rank of non-array is scalar.
    }

    template<class T>  
    inline  
    int 
    Rank( T arg[] ) {
        return 1+Rank(arg[0]);
    }

For example, the fragment below prints 0 1 2.

    float a = 0;
    float b[10];
    float c[20][30];
    printf( "%d %d %d\n", Rank(a), Rank(b), Rank(c) );

The rank computation is similar to the rank computation using class partial specialization, but with important semantic differences. The version using class partial specialization computes the rank of a type as a constant expression, which can be used, for example, to declare an array size. In contrast, the version using function partial ordering computes the rank of an object as a (non-constant) expression. The KAI C++ optimizer will optimize the expression to a constant, but since this is an optimization, and not a language feature, the expression cannot be used where a constant expression is required. E.g., the expression cannot be used to declared an array size. Despite this limitation, the rank computation is nonetheless useful because it can be used in run-time expressions, such as the following:

    int * shape = new int[Rank(c)];

It would seem that rank computations on objects (rather than types) are limited to returning non-constant expressions. But if you like exploring this sort of thing, here is a puzzle. Indeed, consider this puzzle:

Construct a macro Rank(x), that given a auto variable x of arbitrary type, returns the rank of x as a compile-time constant that can be used to declare an array size. For example, macro Rank should make the following snippet legal:

    void foo() {
        float d[40][50][60];
        int x[Rank(d)];
    }

We thought that the puzzle was impossible to solve in Standard C++. However, Esa Pulkkinen found a solution, based on the observation that wrapping a function call inside the operator sizeof provides a way to generate a function call for typing purposes without actually evaluating the function. Here's his solution:

    namespace ArrayRank {

        template <int i> 
        struct Sized { double array[i]; ;

	     template <class T>
        struct rank_type { enum { value = 0 }; };

        template <class T,int N>
        struct rank_type<T[N]> {
            enum { value = 1 + rank_type<T>::value };
        };
	  
        template <class T> 
        Sized< rank_type<T[1]>::value > 
        type_deduction_helper(T x[]) {
            return Sized<rank_type<T[1]>::value>(); 
        }
    }
    #define Rank(x) (sizeof(::ArrayRank::type_deduction_helper(x).array)/sizeof(double))
 

You may be able to gather enough intuition from the examples to charge ahead without reading the rest of this section. However, once empowered, you are likely to expand use of the partial ordering feature until the compiler reports problems of ambiguity. The cure for ambiguity is a deeper understanding of the partial ordering rules. The exact rules that define the partial ordering of functions are given in Section 14.5.5.2 of the ISO Standard. The rules are precise, but mysterious, so here is a more intuitive presentation. The partial ordering is based on a general substitution principle that says that a parametrized entity A is more general than entity B if there exist parameter substitutions for A that make it equal to B. For example, the quadratic formula a*x*x+b*x+c is more general than the linear formula b*x+c, because setting a=0 makes equal to the linear formula.

The partial ordering of function templates is specific to each call site. When determining which of two template functions A and B to use, the compiler first deduces the template parameters that make template B fit the call site. This fitting may involve implicit conversions. If a fit can be found, the compiler then sees if A fits exactly the same function arguments (and implicit conversions) that were used to fit B. No more implicit conversions are allowed during the second fitting. If such a fit can be found, then template A is at least as general as B. The compiler then reverses the roles of A and B to check if B is at least as general as A. If not, then template B is more specialized and is the template selected for the call site.

As with the rest of template matching in C++, the following type differences are ignored when matching the templates:

For instance, consider the following fragment:

    template<class T>
    void g(T) {}              // #1
    template<Class T>
    void g(const T&) {}       // #2

    void caller() {
        int i;
        g(i);                //  Ambiguous
    }

The call is ambiguous because both the top-level reference (&) and const qualifier under it are ignored for matching purposes.

Common Misunderstanding About Partial Specialization

We've fielded multple queries about why code like following does not compile:
    template<class T>                   // Template #1
    T f(T i, T j) { return i + j; }
	     
    template<class T>                   // Template #2
    T f(T i, long j) { return i - j; }

    ...f(2L,2L)... 	// WRONG! 
The erroneous belief is that template #2 is more specialized than template #1, because long is less general than T. This sort of intuition generally works well when each template parameter is used only once in a declaration. However, it is wrong when a template parameter is used twice. The ISO rules say that template #2 is more specialized than template #1 if template #1 can match any instantiation of template #2. But consider these two signatures:
    float f(int,int);				// Declaration #1 
    float f(float,long);			// Declaration #2
Templates #1 and #2 each match declarations #1 and #2 respectively, but not the other. Thus neither template is more specialized than the other.

9.2.11 Farewell to Guiding Declarations

The ISO C++ Standard removes the notion of guiding declarations. Programmers have been using guiding declarations for years without knowing this buzzword. Since absence of guiding declarations may change how programs behave, you need to know how to control whether KAI C++ allows them, what they were, and how to update programs to conform with the Standard.

The easy part is the command line options that control whether KAI C++ recognizes guiding declarations. By default, they are enabled unless in --strict mode. The default can be overridden by the command-line options --guiding_decls and --no_guiding_decls. The reason that they are enabled by default in non-strict is that the majority of existing codes may depend upon them.

The notion of guiding declarations arose from the old C++ model of templates. In this model, a program was completely compiled first, and then if any entities were missing, the compiler looked for templates that could be instantiated as the missing entities. In particular, there was no distinction between an ordinary function and a specialization of a template function. The ordinary function syntax was used to declare specializations. Furthermore, an ordinary function prototype could be used to declare a function that was supposed be instantiated from a template function. For instance, consider the following fragment.

    template<class T> void f( T x ) {}  // #1
    void f( int x );       		// #2 
    void f( void * x );    		// #3
In the old C++ model, if no definition was found to go with declaration on line #2, then the compiler would instantiate the template on the declaration on line #1 to provide the definition. The same would be done for line #3. Lines #2 and #3 were not, however, redundant because they participated in overload resolution. Given a function call, say f(2), the compiler would use lines #2 and #3 to guide overload resolution. Thus those lines were later called guiding declarations.

To summarize: Syntactically, guiding declaration is a declaration that would be a full specialization if it were prefixed by the full specialization syntax template <>. Semantically, it is considered an instance of a template declaration, to be used to guide overload resolution. Guiding declarations are not part of ISO Standard.

Removing guiding declarations from a program requires a little thought. There are four options, and the right one depends upon the programmer's intent.

No change
The lazy option is to not change the program at all. In this case, what were guiding declarations now become declarations for ordinary functions, distinct from the template declaration. The ordinary functions require their own definitions, as they are not considered to be related to the template. Our example, with only the comments updated, is shown below:
template<class T> 
void f( T x ) {}    // Template function 
void f( int x );    // Ordinary function 
void f( void * x ); // Ordinary function
The two ordinary functions require definitions elsewhere, or the program will fail to link.
Use specialization
Another option is to change the declarations to specializations.
template<class T> void f( T x ) {}      // Template function 
template<> void f( int x );    // Specialization of template 
template<> void f( void * x ); // Specialization of template
The two specializations require definitions elsewhere, or the program will fail to link. This is not really much of an improvement over not changing the program. The decision, therefore, should be based upon whether the original intent was to define non-template functions or specializations.
Remove guiding declarations
The third option is to remove the guiding declarations. This is the appropriate option if the original intent was only to provide guidance for overload resolution and not to provide overloaded or specialized definitions. The Standard overload resolution algorithm does not need guiding declarations because it cleanly separates template instantiation from overload resolution. The algorithm does all plausible template instantiation first, and then does overload resolution second. In the old model, these steps were tangled, and the guiding declarations assisted untangling.
Explicit specification
The fourth option is necessary when the guiding declaration is a friend declaration.

For purposes of discussion, below is a sample program that requires a friend declaration. The program prints how much money King Babar has.

#include <iostream> 
using namespace std; 

// Forward declaration of King required for forward declaration of operator<<. 
template<class T> 
class King; 

// Forward declaration of operator<< required for definition of class King. 
template<class T> 
ostream& operator<<( ostream& o, const King<T>& king ); 

template<class T> 
class King { T money; public: King( T value ) : money(value) {} 
    // Guiding declaration 
    friend ostream& operator<<( ostream&, const King<T>& );
}; 
template<class T> 
ostream& operator<<( ostream& o, const King<T>& king ) { 
    // operator<< accesses private member king.money. 
    return o << king.money; 
} 
main() { 
    King<int> Babar(1000000); 
    cout << babar << endl; 
}
Unresolved: operator <<(std::basic_ostream<char,
                        std::char_traits<char>> &, 
                        const King<int>& );
friend ostream& operator<<( ostream&, const King<T>& );
friend ostream& operator<< <T>( ostream&, const King<T>&);

Informally, options 1-4 say that in order to conform to the Standard, you must ``say what you mean.'' In our experience, updating codes to the new template rules has been well worth the improvement in clarity of how template instantiation and overload resolution work.

9.2.12 Explicit Specification of Function Template Arguments

KAI C++ 3.3 implements explicit specification of function template arguments (Section 14.8.1 of Standard). For example, consider the following template function:

    #include <new>

    template<class T> T * QuickAlloc( char* &ptr ) {
	T * result = new(ptr) T;
	ptr += sizeof(T);
	return result;
    }

The template parameter T cannot be deduced from its arguments. Therefore the caller must explicitly specify the template argument, such as shown below:

	QuickAlloc<double>(ptr);

Template member functions may also have their template arguments explicitly specified. Below is an instance.

    template<class A> class Pool {
    public:
	template<class T> T * alloc() {return 0;}
    };

    void f() {
        Pool<double> pool;
        pool.alloc<int>();	// Explicit specification of int
    }

The explicit specification syntax is disallowed for two kinds of member template functions: constructors and conversion operators. For example, the following example is illegal because the caller cannot specify the template arguments for the constructor and the compiler cannot deduce it at any call site.

    struct Foo {
	template<class T> Foo();	// ILLEGAL
    };

The rationale for the restriction on constructor member templates is that the ``obvious'' calling syntax Foo<int>() is already reserved to construct an instance of template class Foo.

The situation for conversion operators is more of a syntactic quirk. Since conversion operators are invoked by the name of their return type instead of a function name, there is technically no function name to which to affix the specification. However, this is never a problem since the type name in the invocation is sufficient to indicate the template argument type. Below is an example from Section 14.5.2, paragraph 5, of the Standard:

      struct A {
	  template <class T> operator T*();
      };
      template <class T> A::operator T*(){ return 0; }
      template <> A::operator char*(){ return 0; }  // specialization
      template A::operator void*();                 // explicit instantiation
      int main()
      {
	  A a;
	  int* ip;

	  ip = a.operator int*();  // explicit call to template operator A::operator int*()
      }

9.2.13 Default Arguments Instantiated Only When Needed

KAI C++ 3.4 instantiates default arguments to template functions only if they are really needed. For example, the following now compiles correctly.
    template <class T> void f(T, T = T()) {}
    struct NoDefault {
	 NoDefault(int) {} 
	 // No default constructor 
    };
    void Demo() {
	f(NoDefault(1),NoDefault(2))
    }
Prior versions of KCC rejected the example because they tried to instantiate the default argument.

9.3 New-Style Casts

A weakness of C is that the same cast notation (T)x is used for a multitude of purposes, some benign and some ``adventuresome.'' The new-style casts distinguish these purposes so that the compiler can report misuses.

dynamic_cast
A dynamic_cast is always type safe. For example, dynamic_cast<Foo*>(x) converts x to a ``pointer to Foo'' if x is a pointer to Foo, a class derived from Foo, or a pointer to a base class of Foo. If the compiler cannot prove that the cast is safe at compilation time, a run-time check is inserted. If the run-time check fails and the cast is to a pointer type, it returns NULL. If the run-time check fails and the cast is to a reference type, the program throws an exception of type std::bad_cast (or if exception-handling is turned off, the program aborts). To use dynamic_cast to cast from a base class to a derived class, the base class must have at least one virtual function, otherwise KCC will report an error during compilation. Unlike old-style cast notation, dynamic_cast allows casting from virtual base classes to derived classes. A dynamic cast cannot be used to remove qualifiers.
const_cast
This cast adds or removes qualifiers on a type. For example, const_cast<Foo*>(x) converts x to a ``pointer to Foo'' if x is a pointer to a (const Foo), but not if x is a pointer to a (const Bar) (unless Bar is a synonym for Foo). Otherwise an error is reported during compilation. Despite its name, const_cast also can add or remove volatile qualifiers. With regard to strong typing, it is always safe to add const and volatile qualifiers with const_cast, but is unsafe to remove those qualifiers.
static_cast
Informally speaking, this cast allows only casts that require no run-time checking and do not remove qualifiers. For example, static_cast<Foo*>(x) converts x to a ``pointer to Foo'' if x is a pointer to a base or derived class of Foo. Otherwise an error is reported during compilation. A static_cast is not necessarily type safe, because it allows unchecked downcasts from base classes to derived classes.
reinterpret_cast
Used for conversions that are unsafe, not portable, or otherwise ``adventuresome.'' Examples are conversions between pointers and integers. KCC currently does not implement the fully untamed reinterpret_cast in that it does not allow casting a pointer to member of one class to a pointer to member of another class if the classes are unrelated. Curiously, reinterpret_cast can not be used for conversions for which the other new-style casts suffice. You cannot simply change any C cast to a reinterpret_cast, unless it really is the dangerous sort loved by the putative ``real programmer.''

Our recommendation is to always use dynamic_cast where possible, as it is typesafe, and has the speed of static_cast when it means the same thing. A static_cast should be reserved for casting downwards in a class hierarchy when a dynamic_cast is not possible or too slow. A const_cast should be reserved for removing const or volatile qualifiers. In general, it should not be used to add such qualifiers because the compiler should automatically add such qualifiers where necessary. As for reinterpret_cast, it is not a subject for polite discussion. Use it to alert readers that your code is impolite.

9.4 Run-time Type Identification (RTTI)

All versions of KAI C++ 3.x support run-time type identification, which includes the operators dynamic_cast and typeid.

The operator typeid(T) allows programs to inspect types of objects. It is similar in syntax the C sizeof operator in that it can be applied to types or expressions. For a type T, the expression typeid(T) returns an object of class std::type_info corresponding to type T. Top-level qualifiers and reference modifiers are not encoded in the std::type_info. Below is an example of this point from the Standard (5.2.8 paragraph 5):

    class D { ... };
    D d1;
    const D d2;
    typeid(d1) == typeid(d2);      // yields true
    typeid(D)  == typeid(const D); // yields true
    typeid(D)  == typeid(d2);      // yields true
    typeid(D)  == typeid(const D&);// yields true

For an expression x, the rules for the value of typeid(x) are a bit complicated. If x is a reference to a class with at least one (possibly inherited) declaration of a virtual functions, then the result corresponds to the actual run-time type of x. Otherwise, the result corresponds to the declared type of x. In both cases, top-level qualifiers and reference modifiers are ignored. For example, consider the following class hierarchy and initializations.

    class X {               // No virtual functions
    };

    class Y: public X {     // Virtual functions
    public:
        virtual void a_function() {}
    };

    class Z: public Y {	    // Inherited virtual functions.
    };

    const X * x = new Z;		
    const Y * y = new Z;
    const Z * z = new Z;

Given these declarations, here's some sample expressions and what KCC does with them. The precise spelling of string returned by method std::type_info::name depends upon the compiler.

typeid(*x).name()
Returns string "X", despite the fact that x really points to a Z. The reason is that *x is declared as a reference to an X, and since class X has no virtual functions, operator typeid returns the declared type.
typeid(*y).name()
Returns string "Z", because *y is declared as a reference to a Y, class Y has a virtual function, and y really points to a Z.
typeid(*z).name()
Returns string "Z", because *z is declared as a reference to a Z, class Z inherits a virtual function, and z really points to a Z.
typeid(y).name()
Returns string "const Y *", because the result for non-reference types is always the declared type. The qualifier const appears because it is under a level of indirection and thus not a top-level qualifier.

Use of operator typeid requires that the header file <typeinfo> (or <typeinfo.h>) be included by the source file. The header file <typeinfo> defines class std:type_info for users as follows:

    namespace std {
        class type_info {
        public:
            virtual ~type_info();
            bool operator==(const type_info& rhs) const;
            bool operator!=(const type_info& rhs) const;
            bool before(const type_info& rhs) const;
            const char* name() const;
        private:
            type_info(const type_info& rhs);
            type_info& operator=(const type_info& rhs);
        };
    }

Copying and assignment of type_info objects is disallowed. All you are allowed to do is compare them or inspect their names. The method before provides a collation order for type_info objects.

9.4.1 Note for SGI Users

RTTI is supported for SGI platforms, except in old 2.05 versions of KCC that are link-compatible with SGI's current C++ compilers.


9.5 Types bool and wchar_t

The ISO C++ Standard types bool and wchar_t are implemented. True and false values are denoted by the keywords true and false respectively. KAI C++ implements bool such that sizeof(bool)==1, but you should not assume that is true of other C++ compilers.

Library support for type wchar_t is not yet part of KCC.

9.6 New Features for Classes

9.6.1 Mutable

The keyword mutable is implemented. You can use it on non-static data member declarations to indicate members that can be changed even through const references. This is handy when you have objects that are conceptually const, but have an internal hidden state. Below is an example class MemoCos that tries to reuse previous computation when possible.

    class MemoCos {
    private:
        mutable double last_x;
        mutable double last_y;
    public:
        MemoCos() : last_x(0.0), last_y(1.0) {}
        double operator()( double x ) const {
            if( last_x!=x ) {
        	// Recompute memorized value.
        	last_y = cos(last_x=x);
            }
            // Use memorized value.
            return last_y;
        }
    };

Notice that operator() is declared const. Without the keyword mutable, it is not possible to modify the data members last_x and last_y.

9.6.2 Definition of Nested Class OutSide Enclosing Scope

The ARM required that methods for a nested class be defined inside the enclosing class scope, which often made nested classes awkward to define. KAI C++ implements the rules in the Standard that allow methods for nested classes to be defined outside the enclosing scope. For example, KAI C++ accepts the following:

    class Outer {
    public:
        class Inner {
        public:
            Inner( int n );
            int value;
        };
    };

    Outer::Inner::Inner( int n ) {
        value = n;
    }

9.6.3 Static Data Member Declarations

Static member constants of integral or enumeration type can have constant initializers. E.g.:

    class Propane {
        enum answer {YES,NO};
        static const answer flammable = YES;
        static const int chain_length = 3;
    };

The primary advantage of doing so is that if initialized this way, the static member member constants can be used anywhere that a integral constant is required. For example, such constants can be used to declare array bounds, as case labels, and as template parameters.

9.6.4 Non-converting Constructors

The keyword explicit can be used to declare constructors that should not be used as implicit conversions. Consider the following example:

    class Gold {
    public:
        Gold( int );
        explicit Gold( char* );
    };

    void Compare( Gold x, Gold y );

    void DoCompare() {
        Compare( 79, Gold("pyrite") );
    }

The declarations of the two constructors tell the compiler that it may implicitly convert an int into a Gold, but not a (char*) into gold. Thus the call above to Compare requires an explicit conversion of "pyrite" into a Gold.

9.6.5 Friend Definitions

Cfront restricted the friend syntax to friend declarations. ISO Standard C++ extends the syntax to allow direct definition of friends inside classes. Below is an example.

    class Color {
    public:
        Color( float r, float g, float b ) : red(r), green(g), blue(b) {}

        // Function opposite is a friend, not a member.
        friend Color opposite( const Color& c ) {
            return Color( 1.0f-c.red, 1.0f-c.green, 1.0f-c.blue );
        };
    private:
        float red, green, blue;
    };

The function opposite is defined inside the class as a friend, not as a member. Here's a fragment showing a use of it:

    Color red(1,0,0);
    Color cyan = opposite(c);

As with members defined inside a class definition, friend definitions are implicitly inline, so they should be used only for small functions that benefit from inlining. Friend definitions work for template classes too.

9.6.6 Covariant Return Types on Virtual Functions

KAI C++ 3.2 supports covariant return types on virtual functions. This feature allows a derived class to override a virtual function with a function whose return type is a subtype of the return type in the base class. Below is an example:

    class Base {
    public:
        virtual Base * clone() const {return new Base(*this);}  
    };

    class Derived: public Base {
    public:
	     Derived * clone() const {return new Derived(*this);}
    };

The method Derived::clone overrides Base::clone. The advantage is more accurate type checking and less need for downcasts.

9.6.7 Class Name Injection

KAI C++ 3.4 supports class name injection. This ISO-mandated feature can break existing code in ways that may be surprising, To control this feature, use the KCC option --[no_]class_name_injection.

Class name is required by Chapter 9, paragraph 2 of the ISO C++ Standard. When a class, say Foo, is defined, the identifer Foo is injected into the scope containing the definition, and the scope of class Foo itself.

The most common sort of code to break are STL iterators that are declared as nested class declarations that inherit from std::iterator. Here's an example of the mischief.

    #include <iterator>

    template <class T>
    struct Collection {
         struct iterator : public std::iterator<std::random_access_iterator_tag,T> {
              T* current;
         };
 
         struct const_iterator : public std::iterator<std::random_access_iterator_tag,T>
         {
              T* current;
              const_iterator (const iterator& bar) : current (bar.current) {}  // Problem
         };
    };

    template struct Collection<int>
KAI C++ 3.4 with --strict will reject this example, complaining that there is no member bar.current. What happened is that when when processing the declaration of parameter bar, KCC had to look up the meaning of the identifier iterator. It searched upwards from the class scope for const_iterator up through its base classes. When it looked at base class std::iterator, it found the class name iterator that was injected when the instance of std::iterator was defined.

The most common symptoms of the name injection issue are complaints about nonexistent members or unallowed access. The quickest way to fix the example is to insert a typedef for iterator inside the definition of const_iterator. E.g., change it to read:

	 ...
         struct const_iterator : public std::iterator<std::random_access_iterator_tag,T>
         {
	      typedef Collection::iterator iterator;	// Clarifying typedef 
              T* current;
              const_iterator (const iterator& bar) : current (bar.current) {}  
         };
	 ...
Then name lookup will find the typedef before proceeding to the base class that caused the problem.

The other way to fix the problem, which perhaps is more clear to casual readers, is to write out iterator as Collection::iterator everywhere inside the definition of const_iterator. We used the latter approach to fix <deque> and <list>, where we learned about the joy of name injection the hard way.

9.6.8 Farewell to Friend Injection

Convervation of injection must be a physical law, because in addition to mandating name injection for class declarations, the ISO rules also take away name injection for friend declarations. For example, KAI C++ 3.4 in strict mode will reject the declaration of ptr in the example below.
    class Hamilton {
	friend class Burr;
	Burr * ptr; 
    };
claiming that Burr is not defined. Indeed it is not, because by the ISO rules the friend declaration makes Burr a friend without injecting it into the scope of class Hamilton. The option --[no_]friend_injection controls this feature.

9.7 Array New and Delete

The array creation operator operator new[] and array destruction operator operator delete[] can now be overloaded. These are separate from their corresponding scalar operators. For example, you can overload all four as shown below.

    class Memory {
    public:
        void * operator new( size_t size );
        void * operator new[]( size_t size );
        void operator delete( void * ptr );
        void operator delete[]( void * ptr );
    };

Debuggers do not yet understand how to demangle the names for array new and array delete. They show up as __nwa__... and __dla__....

9.8 New Features for Statements

9.8.1 Declarations in Tested Conditions

Declarations in the test condition of if, switch, while, and for statements are supported. For example, the following code is legal.

    while( Object * x = getobject() ) {
        x->activate();
    }

9.8.2 Scope of For-Loop Variable

The scope of the variable declared in a for-loop is the loop. In Cfront, it was from the for-loop to the end of the enclosing scope. For example, the following is legal:

    for( int i=0; i<n; i++ ) {
        do_first_thing();
    }

    for( int i=0; i<n; i++ ) {
        do_second_thing();
    }

If you have old code that depends upon the old scope rules, the easiest way to fix it is to move the initialization to before the loop. For example, if the old code looked like this:

    for( int i=0; i<n; i++ ) {
        ...
    }

    if( i ) { // Depends on old scope rule
        ...
    }

it can be fixed by moving the initialization like this:

    int i=0;
    for( ; i<n; i++ ) {
        ...
    }

    if( i ) { // Okay by new scope rule
        ...
    }

In every case that we have encountered in real codes, in the worst case the new scope rule cause KCC to reports errors. In theoretically possible cases where the code is legal with both scope rules, but would behave differently, KCC emits a diagnostic warning.

9.8.3 Return Void

KAI C++ 3.4 allows functions with return type void to return void expressions, for instance:
    void noop(); 
    void much_ado_about_nothing() {return noop();}

9.9 Literal Strings are Const

KAI C++ 3.4 follows the ISO rule that literal strings have type (const char*). Previously, they had type (char*). For instance, the abomination below is now illegal:
    "abc"[2] = 'x';	// ILLEGAL

9.10 Const and Volatile Qualifiers Retained on Rvalues

Const and volatile qualifiers are meaningful on class rvalues. In particular, they are not ignored for function return values. The following code illustrates this point.

    class Quark {
    public:
        void kind() const {cout << "const\n";}
        void kind() {cout << "not const\n";}
    };

    const Quark f1() {return Quark();}
    Quark f2() {return Quark();}

    main() {
        f1().kind(); // Call f1, then print "const"
        f2().kind(); // Call f2, then print "not const"
    }

9.11 Linkage Specification is Part of Type

As Section 7.5 of the Standard says: ``Two functions types with difference language linkages are distinct even if they are otherwise identical.'' This change is a two-edged sword.

extern "C" typedef void (*pcf)(); 
typedef void (*pf)(); // C++ linkage by default 
void example_of_error() { 
    pcf f; 
    pf g; 
    g = f; // ILLEGAL in --strict mode 
}
    #include <iostream> 
    extern "C" typedef void (*pcf)(); 
    extern "C++" typedef void (*pf)(); 
    void report_linkage( pcf ) { 
        std::cout << "C linkage" << std::endl; 
    }
    void report_linkage( pf ) { 
        std::cout << "C++ linkage" << std::endl; 
    }
    void main() { 
        report_linkage((pcf)0); // prints "C linkage" 
        report_linkage((pf)0);  // prints "C++ linkage"
    }

There is an inconsistency in the KAI C++ 3.2 library in that the linkages for functions in the standard library that are standard C functions still have "C" linkage, but the new C++ variants have "C++" linkage. For example, the function std::tan(double) has "C" linkage, but the function std::tan(float) has "C++" linkage. The ISO Standard requires that both have "C++" linkage. The difference is only an issue if your application passes/assigns pointers to these functions.

9.12 Enum Bigger Than Int

KAI C++ 3.4 supports enum types that are bigger than an int. For example, the following now works:
    #include <climits>
    enum Extrema {
	low = LONG_MIN,
	high = LONG_MAX
    };

9.13 New Library Features

The KAI C++ 3.4 library is quite close to the ISO C++ Standard. The major violation that we know of is that C names from vendor's C headers that are supposed to be only in namespace std are also in the global namespace. We tried real hard on that one, and decided discretion was the better part of valor. Wide characters and multibyte characters are also not implemented. Please report any other discrepanicies that you find. This section summarizes the evolution of the library. It is not a tutorial, but merely a reminder that new features exist, and some interfaces have evolved.

The library header files can all be compiled in --strict syntax mode, but --strict does not govern the conformance of the library headers to the standard template library specifications. KAI has added some extra methods (e.g. opfx was added to class ostream) for the sake of backwards compatibility. These ``additions'' are under the control of various macro symbols. Here's a table of the symbols and affected entities.

KAI_NONSTD_ALLOCATOR allocator<void>
KAI_NONSTD_CHAR_TRAITS std::char_traits<char>
KAI_NONSTD_BITSET conversions between bitset and string
KAI_NONSTD_COMPLEX inv
KAI_NONSTD_FSTREAM basic_filebuf(FILE*)
KAI_NONSTD_FUNCTIONAL old STL adaptors (e.g. unary_compose)
KAI_NONSTD_HEAP Header heap.h
KAI_NONSTD_IOS_BASE ios_base::seek_dir
KAI_NONSTD_IOSTREAM ``withassign'' classes
KAI_NONSTD_ITERATOR old ISO drafts of <iterator>
KAI_NONSTD_MAP map, multimap
KAI_NONSTD_OSTREAM Cfront members of ostream
KAI_NONSTD_SET set, multiset
KAI_NONSTD_STREAMBUF Cfront members of basic_streambuf
KAI_NONSTD_UTILITY triple, restrictor
KAI_NONSTD_VECTOR_TRAITS vector
KAI_NONSTD_VALARRAY_TRAITS valarray
Defining the symbol to a nonzero value enables the extensions. This differs from KCC 3.3, which had a single symbol (__KAI_STRICT) that controlled all extensions. The new system lets you pick and choose your departure from ISO practice.

We recommend migrating your code away from these extensions, with the possible exception of KAI_NONSTD_FSTREAM. That one seems to define a constructor that many people have demanded, and we are puzzled as to why ISO does not require it.

If you have trouble porting an existing code to compile under KCC 3.4, please contact us so that we may help. The new KCC library and compiler are way ahead of existing C++ implementations, so we need your guidance in making KCC support both existing and ISO C++ codes.

9.13.1 I/O Streams

In KCC, the stream classes are templatized as required by the Standard. Streams for type wchar_t are not yet supported. See here about how to migrate old codes to the new stream classes.

The ISO C++ classes istream and ostream are much more complicated than the Cfront versions, largely due to the locale feature. As a consequence, the KCC iostreams are slower than Cfront's, but much more general with respect to internationalization. For fast input and output where formatting is not needed, we strongly sugggest using unformatted I/O (Section 27.6.1.3 and 27.6.2.6 of Standard).

While on the subject of streams, it's worth clarifying what opening a stream in ``binary mode'' means. It does not mean that values are written out as raw bits. For instance, the floating point value number 3.41 will still be written out in ASCII, not in IEEE binary format. What ``binary mode'' means, as opposed to ``text mode'', is that the bytes (once formatted) will be written out exactly as is. On Unix systems, ``binary mode'' and ``text mode'' act the same. (The Rationale document for ANSI C says so.) On some other operating systems, ``text mode'' may imply special conversions such as expanding newline delimiters into carriage-return/line-feed sequences.

9.13.1.1 Change in ios_base::sync_with_stdio

Prior to KCC 3.4, the standard stream objects cin, cout, and cerr were always synchronized with stdin, stdout, and stderr respectively. This allowed mixing C and C++ I/O operations on them. In KCC 3.4, they are still synchronized by default, but invoking ios_base::sync_with_stdio(false) will make them unsynchronized. The advantage of unsynchronized streams is that they run about 5x faster for formatted I/O.

9.13.1.2 Change in manipulator setfill

In KCC 3.3, the manipulator setfill had the signature:
template <class charT, class traits>
basic_smanip <typename basic_ios<charT, traits>::int_type, charT, traits>
setfill (typename basic_ios<charT, traits>::int_type);	// KCC 3.3
In KCC 3.4, it has the ISO mandated tyep:
template<class charT>
T5 setfill(charT c) { return __kai::omanip_setfill<charT>(c); }
where T5 represents an internal implementation type.

An unfortunate consequence of this change is that it breaks existing codes that used a non-character argument for setfill. Here's an example:

cout << setfill(65);		// WRONG - argument has type int.
cout << setfill((char)65);	// Right.
cout << setfill('A');		// Right.

9.13.1.3 Change in Template Class istream

getline/gcount no longer interact

In KCC 3.2, the function getline declared in <string> counted characters, as required by the January 1996 draft. The count was accessible via method gcount() on the input stream. Subsequent ISO drafts have removed this feature. Thus to be in line with the December 1996 and final ISO Standard (Section 21.3.7.9, as of KCC 3.3 the aforementioned function getline no long counts characters.

9.13.2 Namespace rel_ops

The header file <utility> now puts the template relational operators in a separate namespace rel_ops, as required by the ISO Standard. To see the reason for the change, consider the definition of template operator<= in the library:

    template <class T>
    inline bool
    operator<= (const T& x, const T& y)
    {
	return !(y<x);
    }

While this definition is correct if type T is totally ordered, it is wrong if type T is partially ordered. By putting the operator in namespace rel_ops, programmers have control over using it.

9.13.3 Traits for Characters

The class char_traits is defined in <char_traits>. This class replaces classes ios_traits and string_char_traits, which disappeared in the December 1996 draft. This change should be noticeable only if your code defines its own traits classes.

9.13.4 Traits for Iterators

The class iterator_traits is defined in <iterator>. Iterator traits remove the need for the old (and circuitous) STL practice of defining auxilary functions for the sake of getting information about an iterator type. For example, the old STL was littered with the following sort of idom, where auxilary function __foo exists only because function foo has no way to ask for the distance type corresponding to iterator type ForwardIterator.

    template <class ForwardIterator, class DistanceType> 
    __foo( ForwardIterator first, ForwardIterator last, Distance*  ) {
        // Use 3rd parameter only for its type.
        Distance d = 0;
        // Set d to distance between first and last.
	distance( first, last, d );
        ...
    }

    template<class ForwardIterator> 
    foo( ForwardIterator first, ForwardIterator  last ) {
        // Need to know distance type.  
        // Old STL has no way to do this directly,
        // so pass result of function distance_type 
        // to auxilarly function __foo.
        __foo( first, distance_type(first,last) );
    }

Below is the correct way to write foo. It uses iterator_traits::difference_type to get the distance type, which is now subsumed by type difference_type in the Standard.

    template<class ForwardIterator> 
    foo( ForwardIterator first, ForwardIterator  last ) {
        // Set d to distance between first and last. 
        iterator_traits<first>::difference_type d =
                     distance( first, last );
        ...
    }

The primary template for iterator_traits<Iterator> in <iterator> expects that the following types be defined by Iterator:

    std::Iterator::iterator_category  
    std::Iterator::value_type         
    std::Iterator::difference_type    
    std::Iterator::reference          
    std::Iterator::pointer        

When Iterator is a pointer type, a partial specialization takes over to do the right thing.

Some functions in original HP STL disappeared in the ISO C++ Standard, namely iterator_category, distance_type, and value_type. Their usage (and associated circuitous auxilary functions) should be replaced by the iterator_traits style.

9.13.5 Allocators for Containers

The KCC 3.2 library container templates allow user-defined allocators. This is an extension by the ISO C++ Standard to the orginal STL containers. For example, the class set is parametrized as set<Key,Allocator>. The default Allocator is an instance of the class std::allocator, defined in <memory>.

In most cases, you should write your allocator as a template class, even if your container is going to hold only one type. For example, for a type set<double,DoubleAllocator> it is not enough for DoubleAllocator to allocate objects of type double only. The reason is that class set needs to allocate internal objects of another type that contain type double as a subobject. It does this by using the equivalent of the following computation to get an allocator for type U from the allocator user_allocator for type double that the user supplied:

    DoubleAllocator::rebind<U>::other internal_allocator(user_allocator);

This expression constructs an allocator object called internal_allocator for objects of type U. For this expression to work, two features of class DoubleAllocator are required:

  1. The expression DoubleAllocator::rebind<U>::other must return the type for an allocator similar to DoubleAllocator, but for type U.
  2. It must be possible to construct the allocator for type U from the allocator for type double.

If your allocator class is a template MyAllocator<T>, then it looks something like below:

    template MyAllocator<class T> {
       ...
       template<class U> MyAllocator( const MyAllocator<U>& alloc ) {
           Construct *this, which should allocate objects of type T 
           using same algorithm/policy as allocatoralloc uses for type U. 
       }

       struct rebind {
	   typedef MyAllocator<U> other;
       };
       ...
   };

   typedef MyAllocator<double> DoubleAllocator;

Of course in principle, you could define Allocator::rebind<U>::other as some other completely different allocator, but in practice it is simplest to define allocator types as template classes so that writing the member type rebind is trivial.

Remember to make sure that MyAllocator<U> allocates memory with alignment appropriate for type U. For example, if writing an allocator for type char, which has no alignment requirement itself, the rebound allocator may be allocating objects with quite different alignment requirements.

As of KCC 3.4, the containers are up to date with respect to their use of member function Allocator::deallocate. The Standard that Allocator::deallocate takes two arguments. Early drafts of the standard had one argument. The two argument form must be used, and the second argument must be correct.

9.13.6 Template Class stack

The Standard version of template class stack differs from the old STL version. The old version took a container class as its argument. The Standard version supported by KCC takes two arguments: the element type and (optionally) the container type. The container type defaults to deque. Here's what the new prototype for class stack looks like:

   namespace std {
       template <class T, class Container = deque<T> > class stack;
   }

So, for instance, below are the old and new ways to write a stack of int.

    stack< deque <int> > old_pile1;         // OBSOLETE STL - not supported by KCC	
    stack< vector <int> > old_pile2;        // OBSOLETE STL - not supported by KCC	
    stack< int > new_pile1;                 // Standard style using default deque for container
    stack< int, vector <int> > new_pile2;   // Standard style with explicit container


9.13.7 Templatized complex

The class complex<T> takes a parameter Tthat describes what type to use for the real and imaginary parts. Here are some sample declarations:

	complex<float> x;     // single-precision complex
	complex<double> y;    // double-precision complex
	complex<int> z;       // Gaussian integer

The Standard defines the behavior of complex<T> only for floating types. The behavior for other types (such as for Gaussian integers) is unspecified by the Standard, but we attempt nonetheless to make complex<T> behave in a reasonable manner for such types.

Judicious use of typedefs can ease the transition to the templatized class complex.

9.13.8 Templatized Conversions Between bitset and basic_string

The members of class std::bitset that permitted conversions to and from strings are now properly templatized in KCC 3.3 and deal with any instance of template std::basic_string. In KCC 3.2, they were restricted to std::string. Specifically, the constructor for bitset that took a std::string argument in KCC 3.2 is now templatized to work for any kind of std::basic_string. Method bitset<N>::to_string that returned a std::string in KCC 3.2 is now templatized to return any instance of std::basic_string.

The good news is that the changes bring std::bitset in agreement with the Standard. The bad news is that the changes break existing KCC 3.2 code that depended on the affected members. To ameliorate the migration problem, you can use -DKAI_NONSTD_BITSET=1 to get the old members.

Here are the details for migrating from KCC 3.2. Because the constructor is now a template member, the compiler is not allowed to invoke certain implicit conversions that were previously allowed. The annoying consequence is that direct construction of a bitset from a (const char*) is now prohibited in examples such as shown below:

	std::bitset<8U> foo("10101011");	// NO LONGER LEGAL

The compiler will report that no constructor of bitset matches. (Explanation for template mavens: The requisite template argument deduction for the std::basic_string temporary ignores implicit conversions.) To repair this sort of code, add an explicit conversion to std::string as shown below.

	std::bitset<8U> foo(std::string("10101011"));

The fix for the change to bitset<N>::to_string is a matter of explicitly specifying the template arguments. For instance, if you had this:

	b.to_string();				// NO LONGER LEGAL

simply add the explicit template arguments like this:

	b.to_string<char,std::char_traits<char>,std::allocator<char> >()


9.13.9 Changes in Template Class valarray

Now Conforms to ISO Standard

The KCC 3.4 version of valarray<T> has been extensively rewritten to conform to the ISO C++ Standard. It is also much faster than the KCC 3.3 version.

Implicit conversion to pointer removed

In KCC 3.2, class valarray<T> could be implicitly converted to a (T*) or (const T*). These members appeared in older drafts, but were removed by ISO in recent drafts. The reason is that the presence of these members caused an overload ambiguity conflict with operator[].

Members removed

The following members were in earlier drafts of <valarray>, but are not part of the final C++ Standard: These members may be enabled by defining KAI_NONSTD_VALARRAY=1.

Copy constructors are private

The ISO C++ Standard requires the copy constructors for slice_array and similar proxies to be ``private''. Sadly, the standard was not tested with a compiler that correctly implements Section 12.2 Paragraph 1 of the standard. The problem was even pointed out in comp.lang.c++ when the second final draft was circulated, to no avail. With a conforming compiler the following is illegal:
    void foo( std::valarray<float> &v, std::slice &s ) {
	v[s] += v[s];
}
because the compiler must be permitted to copy-construct a temporary from the result of v[s]. Thus the specification in the ISO C++ Standard makes class std::valarray practically useless. KCC 3.3 worked around this problem in the draft by declaring these copy constructors ``public''. KCC 3.4 favors standard conformance. To get the old behavior, define KAI_NONSTD_VALARRAY=1.

Class gslice no longer sorts indices

Prior to KCC 3.3, class gslice (provided by Modena) quirkily sorted the generated linear indices. We can find no justification in any recent draft or final standard that justifies this behavior. KAI has completely rewritten gslice, and it no longer exhibits the sorting behavior.

Speed Improvements

The slice and gslice classes are now about twice and three times faster respectively than before.

Recommendations on valarray

We get questions about whether valarray is useful for high-performance computation? In our opinion, the answer is no. There are much better alternatives. Class valarray was an attempt to provide APL/F90 style programming in C++. Section 26.3.1 paragraph 2 claims that:
The valarray array classes are defined to be free of certain forms of aliasing, thus allowing operations on these classes to be optimized.
Unfortunately, the "free of certain forms of aliasing" is really only of interest to vector processors. For modern microprocessors, the advantages of the non-aliasing are overwhelmed by other considerations. Consider the following:
	void add( valarray<float>& a, valarray<float>& b, valarray<float>& c ) {
	    a = b + c;
	}
The compiler has no way of knowing if array a is aliased with b or c. Thus it must dynamically allocate storage for array a. This sort of overhead can swamp the alleged gains. It is true, in theory, that implementations could optimize away temporary arrays in expressions such as:
	a = (b * c) + d;
Indeed, there are template-metaprogramming implementations of valarray that do so. We chose not to use template-metaprogramming for our implementation, because it tends to also cause slow compilation. It is unlikely that we (or anyone else, for that matter) are going to optimize class valarray further because:
  1. Much better alternatives exist (e.g. restrict).
  2. The market for such is small compared to other optimizations that benefit a much larger audience (we have to eat too).

If you are really looking for the ultimate in numerical performance in C++, we suggest using KAI C++ with the Blitz or MTL libraries. In conjunction with KAI C++, they achieve speeds comparable and sometimes in excess of FORTRAN speeds. They rely on template metaprogramming. These libraries are not provided by KAI, but can be downloaded from the Web links given.

9.13.10 Changes in Template Class auto_ptr

The KCC 3.4 version of auto_ptr is based on the final ISO C++ Standard, unlike ealier versions based on earlier drafts. Scott Meyers has an excellent discussion of how auto_ptr ended up the way it is.

Once consequence of the final ISO C++ auto_ptr is that it is no longer considered CopyConstructible (Section 20.1.3 of Standard), thus it is now illegal to put auto_ptr in a standard container. For instance, vector<auto_ptr<T> > is forbidden.

9.13.11 Changes in Template Class allocator

Class std::allocator<T> is faster in KCC 3.4. The price you pay is that the argument to method deallocate that specifies the size of the array must be correct. The signature of method std::allocator<T>::deallocate is:
    void deallocate(pointer p, size_type n); 
The argument n was ignored in KAI C++ 3.3, but used to advantage in KAI C++ 3.4. The new allocator keeps free lists of blocks of deallocated memory, and there are separate lists for different sizes. The size argument determines which list gets the block; indeed that is the Standard's reaons for having the argument. If the size is incorrectly specified as larger than the actual size of the deallocated block, a subsequent call to method allocate might allocate one of these misclassifed blocks, and heap havoc will certainly ensue.

9.13.12 Changes in Template Function get_temporary_buffer

The function std::get_temporary_buffer may be called to allocate another buffer while a previously allocated buffer is still active. This is in conformance to what the ISO Standard says. KCC 3.3 and many other STL implementations still inherit the original STL's use of a single global block of memory for the buffer, which while faster, does not conform to the Standard. (And frankly, was an appalling throwback to days of non-reentrant non-thread-safe code.) The price is that the standard-conforming version is slower, no faster than a malloc.


Next Section         Copyright © 1996-1999. All rights reserved.

E-Mail KAI Technical Support   E-Mail KAI   Contact KAI   

This file last updated on 25 March 1999.