C++ Newsletter/Tutorial Issue 14
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:
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