Issue #014
August, 1996


Contents:

Notes From ANSI/ISO - String Literal Types
Introduction to STL Part 1 - Getting Started
Using C++ as a Better C Part 14 - Function-style Casts
Introduction to Templates Part 6 - Friends


NOTES FROM ANSI/ISO - STRING LITERAL TYPES
Jonathan Schilling, jls@sco.com

[Note: this is the first of a series of columns about the details of
the ANSI/ISO C++ standardization process.  Jonathan Schilling works
for SCO in New Jersey and is a member of the ANSI/ISO C++ committee.
You should not assume that features described in this column are
available in your local C++ compiler.  There is often a lag of a year
or more between feature standardization and that feature showing up in
an actual compiler].

At the most recent ANSI/ISO C++ standards meeting in Stockholm in July,
a major change was made to the type of string literals.  Previously,
string literals were of type char[]; now they are of type const char[].

This repairs a longstanding blemish in C++'s type system.  However, it
has the potential of breaking a lot of existing code.  To lessen the
impact, a new standard conversion has been added to the language, from
string literal to char*.  (The type of wide string literals has also
changed, and a similar standard conversion has been added for them).

The result is that some old code will continue to work, but some won't.
For example:

        char* p = "abc";                // used to compile; still does

        char* q = expr ? "abc" : "de";  // used to compile, now an error

        void f(char*);
        f("abc");                       // used to compile, still does

        void g(char*, int);
        void g(const char*, long);
        g("abc", 7);                    // used to compile, now ambiguous

        template <class T> void h(T);
        template<> void h<char*>(char*);
        h("abc");                       // used to call specialization,
                                        // now calls general template

        try {
                throw "abc";
        }
        catch (char*) {}                // used to catch, now doesn't

The new standard conversion is immediately deprecated, meaning that it
may be removed from the next revision of the standard.  If that
happens, the first and third examples above will become compilation
errors as well.

One possibly confusing thing about this new standard conversion is
that it operates upon a subset of values of a type (literal
constants), rather than on all values of a type (which is more
common).  There is precedent, however, in existing standard
conversions defined for the null pointer constant.  

If you want to write code that will work under both the old and new
rules, you can use just the new type in some contexts:

        const char* p = "abc";
        const char* q = expr ? "abc" : "de";

but in some contexts requiring exact type match both types must be
specified:

        try {
                throw "abc";
        }
        catch (char*) { /* do something */ }
        catch (const char*) { /* do the same thing */ }

Changing the type of string literals is a big change in the language,
which also introduces a significant new incompatibility with C.
Whether the gain is worth the pain is a matter of opinion, but the
ANSI vote was 80% in favor and the ISO vote was unanimous.  It is
expected that compiler vendors will provide a compatibility switch
that gives string literals their old type.


INTRODUCTION TO STL PART 1 - GETTING STARTED

STL stands for Standard Template Library, and is a new feature of C++.
We will be presenting some of the basic features of STL in this and
subsequent issues.  STL may not be available with your local C++
compiler as yet.  The examples presented here were developed with
Borland C++ 5.0.  Third-party versions of STL are available from
companies like ObjectSpace and Rogue Wave, and HP's original
implementation (which may be obsolete) is available free on the
Internet.

To get an idea of the flavor of STL, let's consider a simple example,
one where we wish to create a set of integers and then shuffle them
into random order:

        #include <vector>
        #include <algorithm>
        #include <iostream>

        using namespace std;

        int main()
        {
                vector<int> v;

                for (int i = 0; i < 25; i++)
                        v.push_back(i);

                random_shuffle(v.begin(), v.end());

                for (int j = 0; j < 25; j++)
                        cout << v[j] << " ";
                cout << endl;

                return 0;
        }

When run, this program produces output like:

        6 11 9 23 18 12 17 24 20 15 4 22 10 5 1 19 13 3 14 16 0 8 21 2 7 

There's quite a bit to say about this example.  In the first place,
STL is divided into three logical parts:

        - containers

        - iterators

        - algorithms

Containers are data structures such as vectors.  They are implemented
as templates, meaning that a container can hold any type of data
element.  In the example above, we have "vector<int>", or a vector of
integers.

Iterators can be viewed as pointers to elements within a container.

Algorithms are functions (function templates actually) that operate on
data in containers.  Algorithms have no special knowledge of the types
of data on which they operate, meaning that an algorithm is generic in
its application.

We include header files for the STL features that we want to use.
Note that the headers have no ".h" on them.  This is a new feature in
which the .h for standard headers is dropped.

The next line of interest is:

        using namespace std;

We discussed namespaces in earlier newsletter issues.  This statement
means that the names in namespace "std" should be made available to
the program.  Standard libraries use std to avoid the problem
mentioned earlier where library elements (like functions or class
names) conflict with names found in other libraries.

The line:

        vector<int> v;

declares a vector of integers, and then:

        for (int i = 0; i < 25; i++)
                v.push_back(i);

adds the numbers 0-24 to the vector, using the push_back() member
function.

Actual shuffling is done with the line:

        random_shuffle(v.begin(), v.end());

where v.begin() and v.end() are iterator arguments that delimit the
extend of the list to be shuffled.

Finally, we display the shuffled list of integers, using an overloaded
operator[] on the vector:

        for (int j = 0; j < 25; j++)
                cout << v[j] << " ";
        cout << endl;

This code is quite generic.  For example, we could change:

        vector<int> v;

to:

        vector<float> v;

and fill the vector with floating-point numbers.  The rest of the code
that shuffles and displays the result would not change.

One point to note about STL performance.  The library, at least the
version used for these examples, is implemented as a set of header
files and inline functions (templates).  This structure is probably
necessary for performance, due to the internal use of various helper
functions (for example, begin() in the above example).  Such an
architecture is very fast but can cause code size blowups in some
cases.

We will be saying more about STL in future issues.  The library is not
yet in widespread use, and it's too early to say how it will shake out.


USING C++ AS A BETTER C PART 14 - FUNCTION-STYLE CASTS

In C and C++ (and Java), you can cast one object type to another by
usage like:

        double d = 12.34;

        int i = (int)d;

Casting in this way gets around type system checking.  It may
introduce problems such as loss of precision, but is useful in some
cases.

In C++ it's possible to employ a different style of casting using a
functional notation:

        double d = 12.34;

        int i = int(d);

This example achieves the same end as the previous one.

The type of a cast using this notation is limited.  For example,
saying:

        unsigned long*** p = unsigned long***(0);

is invalid, and would need to be replaced by:

        typedef unsigned long*** T;

        T p = T(0);

or by the old style:

        unsigned long*** p = (unsigned long***)0;

Casting using functional notation is closely tied in with constructor
calls.  For example:

        class A {
        public:
                A();
                A(int);
        };

        void f()
        {
                A a;
                a = A(37);
        }

causes an A object local to f() to be created via the default
constructor.  Then this object is assigned the result of constructing
an A object with 37 as its argument.  In this example there is both a
cast (of sorts) and a constructor call.  If we want to split hairs a
perhaps more appropriate technical name for this style of casting is
"explicit type conversion".

It is also possible have usage like:

        void f()
        {
                int i;

                i = int();
        }

If this example used a class type with a default constructor, then the
constructor would be called both for the declaration and the
assignment.  But for a fundamental type, a call like int() results in
a zero value of the given type.  In other words, i gets the value 0.

The reason for this feature is to support generality when templates
are used.  There may be a template such as:

        template <class T> class A {
                void f()
                {
                        T t = T();
                }
        };

and it's desirable that the template work with any sort of type
argument.

Note that there are also casts of the form "static_cast<T>" and so on,
which we will discuss in a future issue.


INTRODUCTION TO TEMPLATES PART 6 - FRIENDS

In earlier issues we've seen how a template is something like a class,
except that it can be parameterized, that is, type arguments can be
supplied to create an actual class from a template through the process
of instantiation.

In C++ friends are used to give outside functions and classes access
to private members of a class.  Friends can also be used with
templates, in a similar way.  For example:

        template <class T> class A {
                int x;
                friend void f();
        };

        void f()
        {
                A<double> a;

                int i = a.x;
        }

        int main()
        {
                f();

                return 0;
        }

In this example, the function f() gains access to the private members
of all instantiated classes that come from the template A, such as
A<double>, A<char**>, and so on.

In a similar way, a whole class can be granted access to private
members of a template:

        template <class T> class A {
                int x;
                friend class B;
        };

        class B {
        public:
                void f();
        };

        void B::f()
        {
                A<short> a;

                int i = a.x;
        }

        int main()
        {
                B b;

                b.f();

                return 0;
        }

Here, class B is a friend of template A, and so all of B's members can
access the private members of A<short>.

In an earlier issue, we talked about member templates.  With this
feature additional combinations of friends and templates are possible.


ACKNOWLEDGEMENTS

Thanks to Steve Adamczyk, Nathan Myers, Eric Nagler, David Nelson,
Terry Rudd, Jonathan Schilling, and Clay Wilson for help with
proofreading.


SUBSCRIPTION INFORMATION / BACK ISSUES

To subscribe to the newsletter, send mail to majordomo@world.std.com
with this line as its message body:

subscribe c_plus_plus

Back issues are available via FTP from:

        rmii.com /pub2/glenm/newslett

or on the Web at:

        http://www.rmii.com/~glenm

There is also a Java newsletter.  To subscribe to it, say:

subscribe java_letter

using the same majordomo@world.std.com address.

-------------------------

Copyright (c) 1996 Glen McCluskey.  All Rights Reserved.

This newsletter may be further distributed provided that it is copied
in its entirety, including the newsletter number at the top and the
copyright and contact information at the bottom.

Glen McCluskey & Associates
Professional C++ Consulting
Internet: glenm@glenmccl.com
Phone: (800) 722-1613 or (970) 490-2462
Fax: (970) 490-2463
FTP: rmii.com /pub2/glenm/newslett (for back issues)
Web: http://www.rmii.com/~glenm
