10.8. Templates and Containers¶
Templates enable us to specify a range of related (overloaded) functions-called function-template specializations-or a range of related classes-called class-template specializations.
To use function-template specializations, the programmer writes a single function-template definition. Based on the argument types provided in calls to this function, C++ generates separate specializations to handle each type of call appropriately. These are compiled along with the rest of a program’s source code.
All function-template definitions begin with the keyword template followed by formal type parameters to the function template enclosed in angle brackets ( and ); each formal type parameter must be preceded by keyword class or typename. Keywords class and typename used to specify function-template type parameters mean “any built-in type or user-defined type.”
Template-definition formal type parameters are used to specify the kinds of arguments to the function, the return type of the function and to declare variables in the function.
The name of a formal type parameter can be used only once in the type-parameter list of a template header. Formal type-parameter names among function templates need not be unique.
A function template may be overloaded in several ways. We can provide other function templates that specify the same function name but different function parameters. A function template can also be overloaded by providing other non-template functions with the same function name, but different function parameters.
Class templates provide the means for describing a class generically and for instantiating classes that are type-specific versions of this generic class.
Class templates are called parameterized types; they require type parameters to specify how to customize a generic class template to form a specific class-template specialization.
The programmer who wishes to use class-template specializations writes one class template. When the programmer needs a new type-specific class, the programmer uses a concise notation, and the compiler writes the source code for the class-template specialization.
A class-template definition looks like a conventional class definition, except that it is preceded by template class T (or template typename T ) to indicate this is a class-template definition with type parameter T indicating the type of the class to create. The type T is mentioned throughout the class header and member-function definitions as a generic type name.
Member-function definitions outside a class template each begin with template class T (or template typename T ). Then, each function definition resembles a conventional function definition, except that the generic data in the class always is listed generically as type parameter T. The binary scope-resolution operator is used with the class-template name to tie each member function definition to the class template’s scope.
It is possible to use nontype parameters in the header of a class template.
A class for a specific type can be provided to override the class template for that type.
A class template can be derived from a class-template specialization. A class template can be derived from a non-template class. A class-template specialization can be derived from a class template. A non-template class can be derived from a class template.
Functions and entire classes can be declared as friends of non-template classes. With class templates, the obvious kinds of friendship arrangements can be declared. Friendship can be established between a class template and a global function, a member function of another class (possibly a class-template specialization) or even an entire class (possibly a class-template specialization).
Each class-template specialization instantiated from a class template has its own copy of each static data member of the class template; all objects of that specialization share that static data member. And as with static data members of non-template classes, static data members of class-template specializations must be initialized at file scope.
Each class-template specialization gets a copy of the class template’s static member functions.
10.8.1. STL¶
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <list>
#include <algorithm> // for copy
using std::cout;
using std::endl;
using std::string;
template < class T >
void printList( const std::list< T > & listRef );
#define INITSIZE 2
char *ReadLine(char *, FILE *);
int linesize;
int main( int argc, char *argv[] )
{
char *line;
FILE *fp;
string *st;
std::list< string > top;
if( argc < 2 ) {
fp = stdin;
}
else {
if(( fp = fopen( argv[1], "r")) == NULL ) {
fprintf(stderr,"Error: could not open file %s\n", argv[1]);
exit(1);
}
}
linesize = INITSIZE;
if( (line = (char *)malloc(linesize*sizeof(char))) == NULL) {
fprintf( stderr, "Memory Allocation error.\n" );
exit(1);
}
/*
* Read data from file and insert into linked list
*/
while( (line = ReadLine(line, fp)) != NULL ) {
st = new string( line );
top.push_front( *st );
}
if( fp != stdin ) fclose( fp );
/*
* Sort the list
*/
top.sort();
/*
* print the sorted file.
*/
printList( top );
return 0;
}
char *ReadLine(char *buff, FILE *fp)
{
int c, i;
buff[0] = '\0';
buff[linesize - 1] = '\0'; /* mark end of buffer */
for( i = 0; ((c = fgetc(fp) ) != EOF) && (c != '\n'); i++ ) {
if( i == linesize - 1 ) {
linesize *= 2;
if( (buff = (char *)realloc(buff, linesize*sizeof(char))) == NULL) {
fprintf( stderr, "Memory Allocation error.\n" );
exit(1);
}
}
*(buff + i) = (char)c;
}
if( c == EOF && i == 0) return NULL;
*(buff + i) = '\0';
return buff;
}
template < class T >
void printList( const std::list< T > & listRef )
{
if( listRef.empty () )
cout << "List is empty";
else {
std::ostream_iterator< T > output( cout, "\n" );
std::copy( listRef.begin(), listRef.end(), output );
}
}