|
| 24.4 Class Template Instantiation
#include <iostream> using namespace std;
// class template declaration part // test.h template <class any_data_type> class Test { public: // constructor Test(); // destructor ~Test(); // function template any_data_type Data(any_data_type); };
template <class any_data_type> any_data_type Test<any_data_type>::Data(any_data_type Var0) {return Var0;}
// a class template definition part // should be in the same header file with the class template declaration // constructor template <class any_data_type> Test<any_data_type>::Test() {cout<<"Constructor, allocate..."<<endl;}
// destructor template <class any_data_type> Test<any_data_type>::~Test() {cout<<"Destructor, deallocate..."<<endl;}
// the main program int main() { Test<int> Var1; Test<double> Var2; Test<char> Var3; Test<char*> Var4; cout<<"\nOne template fits all data type..."<<endl; cout<<"Var1, int = "<<Var1.Data(100)<<endl; cout<<"Var2, double = "<<Var2.Data(1.234)<<endl; cout<<"Var3, char = "<<Var3.Data('K')<<endl; cout<<"Var4, char* = "<<Var4.Data("The class template")<<"\n\n"; return 0; }
Output:
|
When repackaging the template class, the implementation of the class template is slightly different from the normal class. As mentioned before, the declaration and definition part of the class template member functions should all be in the same header file.
For the previous program example, the repackaging is shown below.
#include <iostream>
using namespace std;
// a class template declaration part
// test.h file
template <class any_data_type>
class Test
{
public:
// constructor
Test();
// destructor
~Test();
// function template
any_data_type Data(any_data_type);
};
template <class any_data_type>
any_data_type Test<any_data_type>::Data(any_data_type Var0)
{return Var0;}
// a class template definition part
// should be in the same header file with the class template declaration
// a constructor
template <class any_data_type>
Test<any_data_type>::Test()
{cout<<"Constructor, allocate..."<<endl;}
// a destructor
template <class any_data_type>
Test<any_data_type>::~Test()
{cout<<"Destructor, deallocate..."<<endl;}
// do not run this program
// make sure there is no error such as typo etc
And the main() program is shown below.
// test.cpp file
// compile and run this program
// the main program
int main()
{
Test<int> Var1;
Test<double> Var2;
Test<char> Var3;
Test<char*> Var4;
cout<<"\nOne template fits all data type..."<<endl;
cout<<"Var1, int = "<<Var1.Data(100)<<endl;
cout<<"Var2, double = "<<Var2.Data(1.234)<<endl;
cout<<"Var3, char = "<<Var3.Data('K')<<endl;
cout<<"Var4, char* = "<<Var4.Data("The class template")<<"\n\n";
return 0;
}

While implementing a class template member functions, the definitions are prefixed by the keyword template < >.
The compiler generates a class, function or static data members from a template when it sees an implicit instantiation or an explicit instantiation of the template. The following program example is an implicit instantiation of a class template.
#include <iostream>
using namespace std;
template <class any_data_type>
class Test
{
public:
// a constructor
Test(){ };
// a destructor
~Test(){ };
// member function templates...
any_data_type Funct1(any_data_type Var1)
{return Var1;}
any_data_type Funct2(any_data_type Var2)
{return Var2;}
};
// do some testing
int main()
{
// implicit instantiation generates class Test<int>...
Test<int> Var1;
// implicit instantiation generates class Test<double>...
Test<double> Var2;
cout<<"Implicit instantiation..."<<endl;
// and generates function Test<int>::Funct1()
cout<<"Var1 = "<<Var1.Funct1(200)<<endl;
// and generates function Test<double>::Funct2()
cout<<"Var2 = "<<Var2.Funct2(3.123)<<endl;
return 0;
}
Output:

From the program example, the compiler generates Test<int> andTest<double> classes andTest<int>::Funct1() andTest<double>::Funct2() function definitions.
The compiler does not generate definitions for functions, non virtual member functions, class or member class that does not require instantiation.
In the program example, the compiler did not generate any definition forTest<int>::Funct2() andTest<double>::Funct1(), since they were not required.
The following is a program example of an explicit instantiation of a class template.
#include <iostream>
using namespace std;
template <class any_data_type>
class Test
{
public:
// a constructor
Test(){ };
// a destructor
~Test(){ };
// a member functions...
any_data_type Funct1(any_data_type Var1)
{return Var1;}
any_data_type Funct2(any_data_type Var2)
{return Var2;}
};
// explicit instantiation of class Test<int>
template class Test<int>;
// explicit instantiation of class Test<double>
template class Test<double>;
// do some testing
int main()
{
Test<int> Var1;
Test<double> Var2;
cout<<"Var1 = "<<Var1.Funct1(200)<<endl;
cout<<"Var2 = "<<Var2.Funct2(3.123)<<endl;
return 0;
}

The following program examples are implicit and explicit instantiation of function templates respectively.
// implicit instantiation
#include <iostream>
using namespace std;
template <class any_data_type>
any_data_type MyMax(any_data_type Var1, any_data_type Var2)
{
return Var1 > Var2 ? Var1:Var2;
}
// do some testing
int main()
{
int p;
char q;
p = MyMax(100, 200);
q = MyMax('k', 'K');
// implicit instantiation of MyMax(int, int)
cout<<"MyMax(100, 200) = "<<p<<endl;
// implicit instantiation of MyMax(char, char)
cout<<"MyMax('k', 'K') = "<<q<<endl;
return 0;
}

// an explicit instantiation
#include <iostream>
using namespace std;
template <class any_data_type>
any_data_type Test(any_data_type Var1)
{
return Var1;
}
// an explicit instantiation of Test(int)
template int Test<int>(int);
// do some testing
int main()
{
cout<<"Var1 = "<<Test(100)<<endl;
return 0;
}

Instantiating the virtual member functions of a class template that does not require instantiation is implementation defined. For example, in the following example,virtual function TestVirt<any_data_type>::Test() is not required, compiler will generate a definition forTestVirt<any_data_type>::Test().
#include <iostream>
using namespace std;
template <class any_data_type>
class TestVirt
{
public:
virtual any_data_type TestFunct(any_data_type Var1)
{return Var1;}
};
// do some testing
int main()
{
// implicit instantiation of TestVirt<int>
TestVirt<int> Var1;
cout<<"Var1.TestFunct(100) = "<<Var1.TestFunct(100)<<endl;
return 0;
}

// an object instantiation Vector<int> Var; // as function parameter int Funct(Vector <float>&); // used in sizeof expression size_t p = sizeof(Vector <char>); // used in class object instantiations class MyTestVector: private Vector<std::string> { }; Vector <Date> Var1; Vector <string> Var2;
|
For example again, let take a look at the max() function of the STL by recreating our own version and name itMyMax(). This general class template is called Primary Template. It should suit to all data types. For example:
#include <iostream>
#include <string>
using namespace std;
template <class any_data_type>
inline any_data_type MyMax(const any_data_type& Var1, const any_data_type& Var2)
{
cout<<"Checking..."<<endl;
return Var1 < Var2 ? Var2 : Var1;
}
// do some testing
int main()
{
int Highest = MyMax(7, 20);
char p = MyMax('x' , 'r');
string Str1 = "Class", Str2 = "Template";
string MaxStr = MyMax(Str1, Str2);
cout<<"The bigger between 7 and 20 is "<<Highest<<endl;
cout<<"The bigger between 'x' and 'r' is "<<p<<endl;
cout<<"The bigger between \""<<Str1<<"\" and \""<<Str2<<"\" is "<<MaxStr<<"\n\n";
const char *Var3 = "Class";
const char *Var4 = "Template";
const char *q = MyMax(Var3, Var4);
cout<<"Logical error, comparing the pointer, not the string..."<<endl;
cout<<"Address of the *Var3 = "<<&Var3<<endl;
cout<<"Address of the *Var4 = "<<&Var4<<endl;
cout<<"The bigger between \""<<Var3<<"\" and \""<<Var4<<"\" is "<<q<<endl;
cout<<"Need specialization here..."<<endl;
return 0;
}

The primary template should be generic to a potentially infinite set of template arguments. Actually, we can narrow down these generic arguments to our specific needs and refer it as user defined specialization or explicit specialization. Implicitly, instantiation should be invoked automatically for the specified data types.
Specialization explicitly fixes all template parameters to a unique template argument. Template specialization will override the template generated code by providing special definitions for specific types.
An explicit specialization looks like a normal template definition except that it must appear after its primary template.
From the previous example, let define specialization for the const char * of the last part of the program example.
Firstly we have to replace every occurrence of any_data_type in the specialization with const char *. The template parameter list also should be empty as shown below:
#include <iostream>
#include <string>
// for strcmp()
#include <cstring>
using namespace std;
// primary template, for all type
template <class any_data_type>
any_data_type MyMax(const any_data_type Var1, const any_data_type Var2)
{
cout<<"Primary template..."<<endl;
return Var1 < Var2 ? Var2 : Var1;
}
// specialization for const char *, empty parameter list
template <>
const char *MyMax(const char *Var1, const char *Var2)
{
cout<<"Specialization..."<<endl;
// comparison for const char *
return (strcmp(Var1, Var2)<0) ? Var2 : Var1;
}
// do some testing
int main()
{
// call primary
int Highest = MyMax(7, 20);
// call primary
char p = MyMax('x' , 'r');
string Str1 = "Class", Str2 = "Template";
// call primary
string MaxStr = MyMax(Str1, Str2);
cout<<"The bigger between 7 and 20 is "<<Highest<<endl;
cout<<"The bigger between 'x' and 'r' is "<<p<<endl;
cout<<"The bigger between \""<<Str1<<"\" and \""<<Str2<<"\" is "<<MaxStr<<"\n\n";
// call specialization
const char *Var3 = "Class";
const char *Var4 = "Template";
const char *q = MyMax(Var3, Var4);
cout<<"The bigger between \""<<Var3<<"\" and \""<<Var4<<"\" is "<<q<<endl;
return 0;
}

Between the primary (general) and the specific specialization, is called partial specializations. This specialization partially fixes their template parameter which applies to a subset of types. It should suit to a portion of data types.
For example we can define a general template called Test<any_data_type>, a partial specialization called Test<any_data_type*> that applies to pointers and a specialization Test<const char*> that applies to const char* exclusively.
A program example:
#include <iostream>
using namespace std;
// general, justice for all type:-)
template <class any_data_type>
any_data_type Test(any_data_type Var1)
{return Var1;}
// partial specialization for all pointers type
template <class any_data_type>
any_data_type * Test(any_data_type *Var2)
{return Var2;}
// specialization, just for const char *
template <>const char * Test(const char *Var3)
{return Var3;}
// do some testing
int main()
{
int p = 5;
// calls Test(any_data_type
int q = Test(p);
double r = Test(3.1234);
cout<<"General types = "<<q<<endl;
cout<<"General types = "<<r<<endl;
// calls Test(any_data_type*)
int *s = Test(&p);
char *t = "Partial lor!";
cout<<"Partial types = "<<s<<endl;
cout<<"Partial types = "<<t<<endl;
// calls Test(const char *)
const char *u = Test("Specialized!");
cout<<"Specialization type = "<<u<<endl;
return 0;
}

As for class template, function template also has specialization.
A program example:
#include <iostream>
using namespace std;
template <class any_data_type>
any_data_type MyMax(any_data_type Var1, any_data_type Var2)
{
return Var1 > Var2 ? Var1:Var2;
}
// specialization of MyMax() for char *
template<>char* MyMax(char* Var3, char* Var4)
{
return strcmp(Var3,Var4)> 0 ? Var3:Var4;
}
int main()
{
cout<<"MyMax(10,20) = "<<MyMax(10,20)<<endl;
cout<<"MyMax('Z','p') = "<<MyMax('Z','p')<<endl;
cout<<"MyMax(1.234,2.345) = "<<MyMax(1.234,2.345)<<endl;
char* Var3 = "Function";
char* Var4 = "Template";
cout<<"\nTesting...."<<endl;
cout<<"Address of *Var3 = "<<&Var3<<endl;
cout<<"Address of *Var4 = "<<&Var4<<endl;
cout<<"MyMax(\"Function\",\"Template\") = "<<MyMax(Var3,Var4)<<endl;
return 0;
}

You may encounter this keyword somewhere, sometime. The typename keyword is used to specify the identifier that follows is a type. In other word, typename keyword tells the compiler that an unknown identifier is a type. Consider the following example:
template <class any_data_type>
class MyClass
{
typename any_data_type::another_data_type * ptr;
//...
};
int main()
{
return 0;
}
Here,typename is used to clarify thatanother_data_type is a type of classany_data_type. Thus, ptr is a pointer to the type any_data_type::another_data_type. Without typename, another_data_type would be considered a static member. Hence,
any_data_type::another_data_type * ptr
Would be interpreted as a multiplication of value another_data_type of type any_data_type withptr.
According to the qualification of another_data_type being a type, any type that is used in place of any_data_type must provide an inner type of another_data_type. For example, the use of type Test as a template argument
MyClass<Test> x;
Is possible only if type Test has an inner type definition such as the following:
class Test
{
typedef int another_data_type;
...
};
In this case, the ptr member of MyClass<Test> would be a pointer to type int. However, the another_data_type could also be an abstract data type such as a class as shown below.
class Test
{
class another_data_type;
...
};
typename is always necessary to qualify an identifier of a template as being a type, even if an interpretation is not a type.
Thus, the general rule in C++ is that any identifier of a template is considered to be a value, except it is qualified by typename keyword, then it is a type.
Apart from this,typename can also be used instead of class in a template declaration:
template <typename any_data_type>
class MyClass
{ };
Well, now you may be ready to create your own template or just proceed to the next Modules, see how these templates used to construct the C++ headers and how to use other readily available classes or function templates in your programs.
The following is a program example compiled usingg++ on Fedora 3 machine presented as an example.
// *******template.cpp**********
#include <iostream>
#include <string>
// for strcmp()
#include <cstring>
using namespace std;
// primary template, for all type
template <class any_data_type>
any_data_type MyMax(const any_data_type Var1, const any_data_type Var2)
{
cout<<"Primary template..."<<endl;
return Var1 < Var2 ? Var2 : Var1;
}
// specialization for const char *, empty parameter list
template <>
const char *MyMax(const char *Var1, const char *Var2)
{
cout<<"Specialization..."<<endl;
// comparison for const char *
return (strcmp(Var1, Var2)<0) ? Var2 : Var1;
}
// do some testing
int main()
{
// call primary
int Highest = MyMax(7, 20);
// call primary
char p = MyMax('x', 'r');
string Str1 = "Class", Str2 = "Template";
// call primary
string MaxStr = MyMax(Str1, Str2);
cout<<"The bigger between 7 and 20 is "<<Highest<<endl;
cout<<"The bigger between 'x' and 'r' is "<<p<<endl;
cout<<"The bigger between \""<<Str1<<"\" and \""<<Str2<<"\" is "<<MaxStr<<"\n\n";
// call specialization
const char *Var3 = "Class";
const char *Var4 = "Template";
const char *q = MyMax(Var3, Var4);
cout<<"The bigger between \""<<Var3<<"\" and \""<<Var4<<"\" is "<<q<<endl;
return 0;
}
[bodo@bakawali ~]$ g++ template.cpp -o template
[bodo@bakawali ~]$ ./template
Primary template...
Primary template...
Primary template...
The bigger between 7 and 20 is 20
The bigger between 'x' and 'r' is x
The bigger between "Class" and "Template" is Template
Specialization...
The bigger between "Class" and "Template" is Template
tenouk fundamental of C++ object oriented tutorial
Source code for this tutorial is available inC++ STL Template source code.
Acomplete C++ Standard Library documentation that includes STL.
Check thebest selling C / C++, STL and UML books at Amazon.com.