Templates
Function Templates :
Functions and classes help to make programs easier to write, safer, and more maintainable. However,
while functions and classes do have all of those advantages, in certain
cases they can also be somewhat limited by C++'s requirement that you
specify types for all of your parameters.
Consider the following example :
int sum(int a, int b) {
return a+b;
}
int main () {
int x=7, y=15;
cout << sum(x, y) << endl;
} // Outputs 22
return a+b;
}
int main () {
int x=7, y=15;
cout << sum(x, y) << endl;
} // Outputs 22
The function works as expected, but is limited solely to integers. Hence it becomes necessary to write a function for each type, such as doubles.
double sum(double a, double b) {
return a+b;
}
It be much more efficient to be able to write one version of sum() to work with parameters of any type. Function templates have us the ability to do that! With function templates, the basic idea is to avoid the necessity of
specifying an exact type for each variable. Instead, C++ provides the capability of defining functions using placeholder types,
called template type parameters.
To define a function template, use the keyword template, followed by the template type definition:
template <class T> //Here, T is a generic data type
Now the generic data type T can be used in the function:
template <class T>
T sum(T a, T b) {
return a+b;
}
int main () {
int x=7, y=15;
cout << sum(x, y) << endl;
} // Outputs 22
T sum(T a, T b) {
return a+b;
}
int main () {
int x=7, y=15;
cout << sum(x, y) << endl;
} // Outputs 22
The function returns a value of the generic type T, taking two parameters, also of type T.
The same function can be used with other data types, for example doubles:
template <class T>
T sum(T a, T b) {
return a+b;
}
int main () {
double x=7.15, y=15.54;
cout << sum(x, y) << endl;
} // Outputs 22.69
T sum(T a, T b) {
return a+b;
}
int main () {
double x=7.15, y=15.54;
cout << sum(x, y) << endl;
} // Outputs 22.69
Function templates also make it possible to work with multiple generic data types. Define the data types using a comma-separated list. Now, creating a function that compares arguments of varying data types (an int and a double), and prints the smaller one :
template <class T, class U>
T smaller(T a, U b) {
return (a < b ? a : b);
}
T smaller(T a, U b) {
return (a < b ? a : b);
}
In the main function, templates can be used as a substitute for many data types :
template <class T, class U>
T smaller(T a, U b) {
return (a < b ? a : b);
}
int main () {
int x=72;
double y=15.34;
cout << smaller(x, y) << endl;
} // Outputs 15
T smaller(T a, U b) {
return (a < b ? a : b);
}
int main () {
int x=72;
double y=15.34;
cout << smaller(x, y) << endl;
} // Outputs 15
T is short for Type, and is a widely used name for type parameters. It's not necessary to use T,
however; the required type parameters can be declared using any identifiers. The only terms needed to avoid are C++ keywords.
Remember that when you declare a template parameter, you absolutely must use it in your function definition. Otherwise, the compiler will complain!
Just as function templates can be defined, class templates can also be defined, allowing classes to have members that use template parameters as types.
The same syntax is used to define the class template:
template <class T>
class MyClass {
//definition of class
};
As an example, consider a class Pair, that will be holding a pair of generic type.
template <class T>
class Pair {
private:
T first, second;
public:
Pair (T a, T b):
first(a), second(b) {
}
};
A specific syntax is required in case the member functions are defined outside of the class - for example in a separate source file. The generic type needs to be specified in angle brackets after the class name.
For example, to have a member function bigger() defined outside of the class, the following syntax is used:
template <class T>
class Pair {
private:
T first, second;
public:
Pair (T a, T b):
first(a), second(b){
}
T bigger();
};
template <class T>
T Pair<T>::bigger() {
return (first>second ? first : second);
}
The ternary operator compares the two variables, returning the greater one.
To create objects of the template class for different types, specify the data type in angle brackets, as when defining the function outside of the class. Create a Pair object for integers :
Pair <int> obj(11, 22);
cout << obj.bigger();
// Outputs 22
The same class can be used to create an object that stores any other type.
Pair <double> obj(23.43, 5.68);
cout << obj.bigger();
// Outputs 23.43
In case of regular class templates, the way the class handles different data types is the same; the same code runs for all data types. Template specialization allows for the definition of a different implementation of a template when a specific type is passed as a template argument.
For example, the character data type might need to be handled in a different manner than numeric data types. To demonstrate how this works, first create a regular template.
template <class T>
class MyClass {
public:
MyClass (T x) {
cout <<x<<" - not a char"<<endl;
}
};
As a regular class template, MyClass treats all of the various data types in the same way.
To specify different behavior for the data type char, create a template specialization.
template <class T>
class MyClass {
public:
MyClass (T x) {
cout <<x<<" - not a char"<<endl;
}
};
template < >
class MyClass<char> {
public:
MyClass (char x) {
cout <<x<<" is a char!"<<endl;
}
};
First of all, notice that the class name is preceded with template<>, including an empty parameter list. This is because all types are known and no template arguments are required for this specialization, but still, it is the specialization of a class template, and thus it requires to be noted as such.
But more important than this prefix, is the <char> specialization parameter after the class template name. This specialization parameter itself identifies the type for which the template class is being specialized (char).
In the example above, the first class is the generic template, while the second is the specialization.
If necessary, your specialization can indicate a completely different behavior from the behavior of the generic template.
The next step is to declare objects of different types and check the result:
int main () {
MyClass<int> ob1(42);
MyClass<double> ob2(5.47);
MyClass<char> ob3('s');
}
/* Output:
42 - not a char
5.47 - not a char
s is a char!
*/
The generic template worked for int and double. However, the template specialization was invoked for the char data type.
Keep in mind that there is no member "inheritance" from the generic template to the specialization, so all members of the template class specializations must be defined on their own.
Comments
Post a Comment