6.8 Template Overview
To gain some perspective, let's begin with a general example that shows how templates work. We'll explain concepts from the example throughout the section, though you'll probably follow this without difficulty now:
#include <iostream>
#include <rw/cstring.h>
#include <rw/tvdlist.h>
int main()
{
// Declare a linked list of strings
RWTValDlist<RWCString> stringList;
RWCString sentence;
// Add words to the list
stringList.insert("Templates");
stringList.insert("are");
stringList.insert("fun!");
// Now use standard iterators to build a sentence
RWTValDlist<RWCString>::const_iterator iter = stringList.begin();
if (iter != stringList.end()) {
sentence.append(*(iter++));
}
while (iter != stringList.end()) {
sentence.append(" ");
sentence.append(*(iter++));
}
// Display the result
std::cout << sentence << std::endl;
return 0;
}
Output:
Templates are fun!
The preceding example demonstrates the basic operation of templates. Using the collection class template RWTValDList, we instantiate the object stringList, simply by specifying type RWCString. The template gives us complete flexibility in specifying the type of the list; we don't write code for the object, and the Essential Tools Module does not complicate its design with a separate class RWTValDListofRWCString. Without the template, we would be limited to types provided by the program, or forced to write the code ourselves.
6.8.1 Template Naming Convention
You'll notice that the collection class template
RWTValDlist<T,A> in the example follows a unique format. In the Essential Tools Module, all templates have class names starting with
RWT, for
Rogue
Wave
Template, followed by a three letter code:
Isv | Intrusive lists |
Val | Value-based |
Ptr | Pointer-based |
Hence,
RWTValOrderedVector<T,A> is a value-based template for an ordered vector of type-name
T.
RWTPtrMultiMap<K,T,C,A> is a pointer-based template based on the C++ Standard Library
multimap class. Special characteristics may also modify the name, as in
RWTValSortedDlist<T,C,A>, a value-based doubly-linked template list that automatically maintains its elements in sorted order.
6.8.2 Value vs. Reference Semantics in Templates
The Essential Tools Module collection class templates can be either value-based or pointer-based. Value-based collections use
value semantics, maintaining copies of inserted objects and returning copies of retrieved objects. In contrast, pointer-based collections use
reference semantics, dealing with pointers to objects as opposed to the objects themselves. See
Section 6.3 for other examples of value and reference semantics.
Templates offer you a choice between value and reference semantics. In fact, in most cases, you must choose between a value-based or a pointer-based class; for example, either
RWTValOrderedVector<T,A>, or
RWTPtrOrderedVector<T,A>.
Your choice depends on the requirements of your application. Pointer-based templates are a good choice for maximizing efficiency for large objects, or if you need to have the same group of objects referred to in several ways, requiring that each collection class point to the target objects, rather than wholly contain them.
6.8.2.1 An Important Distinction
There is a big difference between a value-based collection of pointers, and a pointer-based collection class. You can save yourself difficulty by understanding the distinction.
For example, declaring:
// value-based list of RWCString pointers
RWTValDlist< RWCString* > words;
gives you a value-based list, where the values are of type pointer to
RWCString. The collection class will concern itself only with the pointers, never worrying about the actual
RWCString objects they refer to. Now consider:
RWCString* ptr1 = new RWCString ("foo");
words.insert (ptr1);
RWCString* ptr2 = new RWCString ("bar");
std::cout << words.occurrencesOf (ptr2); // Prints 0
The above code prints 0 because the memory locations of the two string objects are different, and the collection class is comparing only the values of the pointers themselves (their addresses) when determining the number of occurrences.
Contrast that with the following:
// pointer-based list of RWCStrings
RWTPtrDlist<RWCString> words;
RWCString* ptr1 = new RWCString ("foo");
words.insert (ptr1);
// Different object, same string
RWCString* ptr2 = new RWCString ("foo");
std::cout << words.occurrencesOf (ptr2); // Prints 1
Here the collection class is parameterized by
RWCString, not
RWCString*, showing that only
RWCString objects, not pointers, are of interest to the list. But because it is a pointer-based collection class, communicating objects of interest is done via pointers to those objects. The collection class knows it must dereference these pointers, as well as those stored in the collection class, before comparing for equality.
6.8.3 Intrusive Lists in Templates
For a collection class of type-name T, intrusive lists are lists where type T inherits directly from the link type itself. (See Stroustrup, The C++ Programming Language, Second Edition, Addison-Wesley, 1991, for a description of intrusive lists.) The results are optimal in space and time, but require you to honor the inheritance hierarchy. The disadvantage is that the inheritance hierarchy is inflexible, making it slightly more difficult to use with an existing class. For each intrusive list class, the Essential Tools Module offers templatized value lists as alternative non-intrusive linked lists.
Note that when you insert an item into an intrusive list, the actual item, not a copy, is inserted. Because each item carries only one link field, the same item cannot be inserted into more than one list, nor can it be inserted into the same list more than once.