Function Templates
|
Introduction
|
In previous programs, we found out that we could pass
any type of value, based on one of the primitive data types, to the WriteLine()
method of the Console class and it would appropriately display the value.
Here are examples:
using namespace System; // Display the value of an integer void Show(int value) { Console::WriteLine(value); } // Display the value of a double-precesion value void Show(double value) { Console::WriteLine(value); } // Display the value of a character void Show(__wchar_t value) { Console::WriteLine(value); } int main() { // Call the version of the function that displays an integer Show(246); // Call the version of the function that displays a character Show('G'); // Call the version of the function that displays a decimal Show(355.65); return 0; }
This would produce:
246 G 355.65 Press any key to continue . . .
We passed a constant value directly to the function when we
called it. Remember that you can also first declare a variable, assign it a
value, and then pass that variable to the function. Here are examples:
int main() { // Call the version of the function that displays an integer int Value1 = 246; Show(Value1); // Call the version of the function that displays a character __wchar_t Value2 = L'G'; Show(Value2); // Call the version of the function that displays a decimal double Value3 = 355.65; Show(Value3); return 0; }
Although this is based on the concept of function
overloading, another way you can solve this type of problem is to create one
function that doesn't know the type of value that would be passed to it but the
function is equipped to process the value appropriately. Based on the above
program, you can create one function that takes an argument and it displays its
value. To do this, at the time you are defining the function, you only let it
know that it would receive an argument but you don't specify the type of value that it will
process. This the basis of templates in C++.
|
- Start Microsoft Visual C++ if necessary.
To create a new program, on the Start Page, click the Project button on the right side of Create - In the Templates list, click Win32 Console Application
- Set the name to CommercialStore1
- Click Next
- Click Empty Project and click Finish
- On the main menu, click Project -> Add New Item
- In the Templates list, click C++ File (.cpp)
- Set the name of the file to Exercise and press Enter
- Complete the file as follows:
#include <iostream> using namespace std; typedef double ItemType; struct CItem { ItemType Item; CItem *Next; }; CItem * Head = NULL; int Size; int Count() { return Size; } int Add(CItem * NewItem) { CItem * Sample = new CItem; Sample = NewItem; Sample->Next = Head; Head = Sample; return Size++; } CItem * Retrieve(int Position) { CItem * Current = Head; for(int i = Count() - 1; i > Position && Current != NULL; i--) { Current = Current->Next; } return Current; } void ShowItem(ItemType item) { cout << "Item: " << item << endl; } int main() { CItem * Part; Part = new CItem; Part->Item = 97.43; Add(Part); Part = new CItem; Part->Item = 274.87; Add(Part); Part = new CItem; Part->Item = 8.7873; Add(Part); Part = new CItem; Part->Item = 2764.4; Add(Part); Part = new CItem; Part->Item = 92.4662; Add(Part); Part = new CItem; Part->Item = 66800.85; Add(Part); cout << "-=- List of Items -=-" << endl; for(int i = 0; i < Count(); i++) { CItem * One = Retrieve(i); ShowItem(One->Item); } cout << "\nNumber of Items: " << Count() << "\n\n"; return 0; }
- Exercise the application to see the result
-=- List of Items -=- Item: 97.43 Item: 274.87 Item: 8.7873 Item: 2764.4 Item: 92.4662 Item: 66800.9 Number of Items: 6 Press any key to continue . . .
- Close the DOS window
A template function is a function that can process a value whose type
is known only when the variable is accessed. To create a function template, use
the following formula:
template <class Parameter> ReturnType FunctionName(Argument(s)) { }
The template keyword, the class keyword, the
<, and the > operators are required. On the right side of the class
keyword, type a name that follows the rules of names in C++. Most programmers
simply use the letter T. In our example, we use the Parameter name. After that, define the function as we have done so far.
Here is an example:
template <class TypeOfValue> void Show() { }
In our lesson and in the examples, the Parameter
factor will be referred to as template parameter or parameter type and sometimes
simply as parameter.
Passing the Template Parameter as Argument
|
To indicate that the function will process a value that is
based on the parameter, that is, the type on the right side of the class keyword, you should pass
it as
argument to the function. You do this by preceding the name of the argument with
the Parameter factor. Here is an example:
template <class TypeOfValue>
void Show(TypeOfValue Argument)
{
}
In the body of the function, you can then process the Parameter as you see fit. At a minimum, and based on our earlier program, you can
simply display the value by passing it to the Console::WriteLine()
method. Here is an example:
template <class TypeOfValue> void Show(TypeOfValue value) { Console::WriteLine(value); }
|
- To create a function template, change the ShowItem() function as follows:
template <class T> void ShowItem(T item) { cout << "Item: " << item << endl; }
- Execute the application and notice that it works fine
Calling a Function Template
|
As mentioned earlier, one of the particularities of a
function template is that, at the time it is defined, the function doesn't know the type
of the parameter. This means that, when calling the function, you must make sure
you clearly specify the type of value that will be processed. You can do this by
directly passing (a constant of) the type of value that the function will
process. Here are different
examples of calling our Show() function:
using namespace System; template <class TypeOfValue> void Show(TypeOfValue value) { Console::WriteLine(value); } int main() { // Call the function and pass it an integer value Show(246); // Call the function and pass it a character value Show(L'G'); // Call the function and pass it a double-precision value Show(355.65); return 0; }
When complied and executed, this program would produce:
246 71 355.65 Press any key to continue . . .
As an alternative, you can type the name of the function,
followed by angle brackets. Inside of the brackets, enter the data type of the
value that will be processed. After the angle brackets, open the parentheses
and, inside of them, type the constant value that will be processed. Here are
examples:
using namespace System; template <class TypeOfValue> void Show(TypeOfValue value) { Console::WriteLine(value); } int main() { // Call the function and pass it an integer value Show<int>(246); // Call the function and pass it a character value Show<__wchar_t>(L'G'); // Call the function and pass it a double-precision value Show<double>(355.65); return 0; }
Remember that you can also first declare a variable, assign
it a value, and then pass that variable to the parentheses of the function. Here
are examples:
using namespace System; template <class TypeOfValue> void Show(TypeOfValue &value) { Console::WriteLine(value); } int main() { // Call the function and pass it an integer value int a = 246; Show<int>(a); // Call the function and pass it a character value __wchar_t u = L'G'; Show<__wchar_t>(u); // Call the function and pass it a double-precision value double x = 355.65; Show<double>(x); return 0; }
You can also declare the value as a constant before passing
it to the function.
Function Templates and Pointers
|
A Template Parameter as a Reference
|
As you can use references for a regular variable, you can
create a template function parameter as a reference. To do this, in the
parentheses of the function, precede the name of the parameter with the &
operator. Here is an example:
template <class TypeOfValue>
void Show(TypeOfValue &value)
{
Console::WriteLine(value);
}
When calling the function, make sure you pass the argument
as a reference. Here are examples:
using namespace System; template <class TypeOfValue> void Show(TypeOfValue &value) { Console::WriteLine(value); } int main() { // Call the function and pass it an integer value int a = 246; int &b = a; Show<int>(b); // Call the function and pass it a character value __wchar_t u = L'G'; __wchar_t &v = u; Show<__wchar_t>(v); // Call the function and pass it a double-precision value double x = 355.65; double &y = x; Show<double>(y); return 0; }
A Template Parameter as a Pointer
|
Instead of declaring a template parameter as a regular type,
you can create it as a pointer. To do this, precede the Argument factor
with an asterisk. Remember that, when using the parameter, you must process it
as a pointer. Here is an example:
template <class TypeOfValue> void Show(TypeOfValue *value) { Console::WriteLine(*value); }
When calling the function, apply the rules of pointers
passed to functions. Here are examples:
using namespace System; template <class TypeOfValue> void Show(TypeOfValue *value) { Console::WriteLine(*value); } int main() { // Call the function and pass it an integer value int a = 246; Show<int>(&a); // Call the function and pass it a character value __wchar_t u = L'G'; Show<__wchar_t>(&u); // Call the function and pass it a double-precision value double x = 355.65; Show<double>(&x); return 0; }
Remember that you can first declare a pointer variable,
initialize it, and then pass it to the function. Here are examples:
using namespace System; template <class TypeOfValue> void Show(TypeOfValue *value) { Console::WriteLine(*value); } int main() { // Call the function and pass it an integer value int *a = new int(246); Show<int>(a); // Call the function and pass it a character value __wchar_t *u = new __wchar_t(L'G'); Show<__wchar_t>(u); // Call the function and pass it a double-precision value double *x = new double(355.65); Show<double>(x); return 0; }
A Template Parameter as a Handle
|
If you declare a variable as a regular value, it would be
stored in the stack. If you declare it as a pointer, it would be stored in the native heap. If
you want to process a variable in the managed heap, create it as handle,
applying the rules we have learned in previous lessons. For a template, pass the
parameter as a handle. If you want to display it on the console, pass its
pointer to the Console::WriteLine() method. This can be done as follows:
using namespace System; template <class TypeOfValue> void Show(TypeOfValue ^ value) { Console::WriteLine(value); }
Before calling the method, make sure you have created a
variable as a handle and pass it to the function. This would be done as follows:
int main() { // Call the function to display the number of pages of a book int ^ a = gcnew int(704); Console::Write(L"Number of Pages: "); Show<int>(a); // Call the function to display the character gender __wchar_t ^ u = gcnew __wchar_t(L'M'); Console::Write(L"Employee Gender: "); Show<__wchar_t>(u); // Call the function to display somebody's hourly salary double ^ x = gcnew double(18.48); Console::Write(L"Hourly Salary: "); Show<double>(x); return 0; }
As you should know already, you can pass a handle to String
to the Console::WriteLine() method to display on the console. In the same
way, you can pass a String value to a template function. Here is an
example:
using namespace System; template <class TypeOfValue> void Show(TypeOfValue ^ value) { Console::Write(value); } int main() { // Call the function to display the number of pages of a book String ^ strMessage = L"Number of Pages: "; int ^ a = gcnew int(704); Show(strMessage); Show(a); Console::WriteLine(); // Call the function to display the character gender strMessage = L"Employee Gender: "; __wchar_t ^ u = gcnew __wchar_t(L'M'); Show(strMessage); Show(u); Console::WriteLine(); // Call the function to display somebody's hourly salary strMessage = L"Hourly Salary: "; double ^ x = gcnew double(18.48); Show(strMessage); Show<double>(x); Console::WriteLine(); return 0; }
This would produce:
Number of Pages: 704 Employee Gender: M Hourly Salary: 18.48 Press any key to continue . . .
A Function Template With Various Parameters
|
Just like a function can take one argument, it can take
various template parameters. You can pass one argument as a known type and the other as a
template parameter. Here is an example:
using namespace System; template <class TypeOfValue> void Show(String ^ msg, TypeOfValue value) { Console::WriteLine(L"{0}: {1}", msg, value); } int main() { // Call the function and pass it an integer value Show(L"Integer", 246); // Call the function and pass it a character value Show(L"Character", 'G'); // Call the function and pass it a double-precision value Show(L"Decimal", 355.65); return 0; }
This would produce:
Integer: 246 Character: 71 Decimal: 355.65 Press any key to continue . . .
In the same way, you can pass different parameters to
the function, and you can pass different known types and different parameter
types, in any order of your choice.
Although we directly passed the values to the function when
calling it, you can first declare a variable before passing it to the function.
Here are examples:
using namespace System; template <class TypeOfValue> void Show(String ^ msg, TypeOfValue value) { Console::WriteLine(L"{0}: {1}", msg, value); } int main() { // Call the function and pass it an integer value const int iValue = 246; Show(L"Integer", iValue); // Call the function and pass it a character value const __wchar_t cValue = L'G'; Show(L"Character", cValue); // Call the function and pass it a double-precision value const double dValue = 355.65; Show(L"Decimal", dValue); return 0; }
The other concepts we have reviewed so far are still valid.
For example, you can pass the parameter by value, by reference, as a
pointer, as a constant reference, or as a constant pointer. Here is the
parameter passed as a constant reference:
template <class TypeOfValue> void Show(String ^ msg, const TypeOfValue &value) { Console::WriteLine(L"{0}: {1}", msg, value); }
You can also involve the argument in conditional statements
as you see fit.
Class Templates
|
Introduction
|
Like a function, a class can be created as a template. In
fact, the class is more flexible than the function. To create a class template
that uses a constant value for the parameter, use the following formula:
template <PrimitiveDataType ParameterName> Options class ClassName { }
The template and class keywords are required,
so are the < and the > operators. Also, the curly brackets that delimit a
class must be specified. The Options factor can be an appropriate combination of
public, private, ref, and/or value keywords. The class keyword is followed by
the name of the class, followed by the delimiting curly brackets of a class.
|
- To create a new program, on the main menu, click the File -> New -> Project...
- In the Templates list, click CLR Empty Project
- Set the name to CommercialStore2
- Click OK
- On the main menu, click Project -> Add Class
- In the Templates list, click C++ Class and click Add
- Set the Class Name to CListOfItems and click Finish
- Change the header file as follows:
#pragma once typedef double ItemType; public value struct CItem { ItemType Item; CItem ^ Next; }; public ref class CListOfItems { private: int Size; public: // List Initialization CListOfItems(void); CItem ^ Head; // List Finalization ~CListOfItems(); !CListOfItems(); // Operations on list int CListOfItems::Count(); int CListOfItems::Add(CItem ^ item); CItem ^ CListOfItems::Retrieve(int Position); };
- In the Solution Explorer, double-click ListOfItems.cpp and change it as
follows:
#include "ListOfItems.h" CListOfItems::CListOfItems(void) : Head(nullptr), Size(0) { } CListOfItems::~CListOfItems() { delete Head; } CListOfItems::!CListOfItems() { } int CListOfItems::Count() { return Size; } int CListOfItems::Add(CItem ^ NewItem) { CItem ^ Sample = gcnew CItem; Sample = NewItem; Sample->Next = Head; Head = Sample; return Size++; } CItem ^ CListOfItems::Retrieve(int Position) { CItem ^ Current = Head; for(int i = Count() - 1; i > Position && Current != nullptr; i--) { Current = Current->Next; } return Current; }
- On the main menu, click Project -> Add New Item...
- In the Templates list, click C++ File (.cpp)
- Set the name of the file to Exercise and press Enter
- Complete the file as follows:
#include "ListOfItems.h" using namespace System; int main() { CListOfItems ^ Items = gcnew CListOfItems; CItem ^ Part; Part = gcnew CItem; Part->Item = 97.46; Items->Add(Part); Part = gcnew CItem; Part->Item = 8844.82; Items->Add(Part); Part = gcnew CItem; Part->Item = 108.7873; Items->Add(Part); Part = gcnew CItem; Part->Item = 2764.48; Items->Add(Part); Part = gcnew CItem; Part->Item = 70062.83; Items->Add(Part); Console::WriteLine(L"-=- List of Items -=-"); for(int i = 0; i < Items->Count(); i++) { CItem ^ One = Items->Retrieve(i); Console::WriteLine(L"Item: {0}", One->Item); } Console::WriteLine(L"\nNumber of Items: {0}\n", Items->Count()); return 0; }
- Complete the
A Template Parameter as a Constant Type
|
When creating a class template using our formula, the PrimitiveDataType factor must be one of the
integer data types built in the C++ language (short, Byte, char,
__wchar_t, etc). It cannot be a floating-point type (float, Single, or
double). The data type is followed by a name for the parameter. Outside of the >
operator, you can specify such factors as the public, the private, the value,
and/or the ref keywords, following the same rules applied to the creation of a
class in the previous lessons.
Here is an example of a class:
template <__wchar_t c>
public ref class Description
{
public:
Description();
Description(__wchar_t msg)
{
}
~Description();
void Show();
};
It's important to remember that, with this class creation,
the c parameter is a constant. If you define a method in the body of the class,
you can use the template parameter as you see fit. Here is an example:
template <__wchar_t c>
public ref class Description
{
public:
Description();
Description(__wchar_t msg)
{
}
~Description();
void Show();
};
If you implement a method
outside of the body of the class, precede it with the declaration of
template <PrimitiveDataType ParameterName>
Then, after typing the name of the class and before the ::
operator, enclose the parameter name within the < and > operators. Here is
an example:
template <__wchar_t c> Description<c>::Description() { }
When processing the class, you can involve the template
parameter as you want. For example, you can pass it to the (cout class or
the) Console::WriteLine() method to display it in the console. You can
also involve the parameter in an appropriate operation, such as checking it
using a conditional operator. Here is an example:
template <__wchar_t c> void Description<c>::Show() { switch(c) { case L'i': case L'I': Console::WriteLine(L"Integer"); break; case L'c': case L'C': Console::WriteLine(L"Character"); break; case L'f': case L'F': case L'd': case L'D': Console::WriteLine(L"Decimal"); break; } }
Because the template parameter is treated as a constant, you
can formally declare it using the const keyword.
Class Template Declaration
|
After creating a class template, you can use it by primarily
declaring a variable. To declare a variable of a class template in the stack,
you can use the following formula:
ClassName<ValueType> VariableName;
The < operator, the > operator, and the ; are
required. Like any declaration, you start with the name of the type, in this
case the name of the class. Between the angle brackets, specify the same
primitive type you used when creating the class. End the stack declaration by
the name of the variable. After this declaration, you can use the variable as
you see fit. Here is an example:
using namespace System; template <__wchar_t c> public ref class Description { public: Description(); Description(__wchar_t msg) { } ~Description(); void Show(); }; template <__wchar_t c> Description<c>::Description() { } template <__wchar_t c> Description<c>::~Description() { } template <__wchar_t c> void Description<c>::Show() { switch(c) { case L'i': case L'I': Console::WriteLine(L"Integer"); break; case L'c': case L'C': Console::WriteLine(L"Character"); break; case L'f': case L'F': case L'd': case L'D': Console::WriteLine(L"Decimal"); break; } } int main() { // Call the function and pass it an integer value Description<L'i'> iDscr; iDscr.Show(); // Call the function and pass it a character value Description<L'c'> cDscr; cDscr.Show(); // Call the function and pass it a double-precision value Description<L'f'> dDscr; dDscr.Show(); return 0; }
You can also declare the class variable on the managed heap.
To create a handle for the class, in both sides of the assignment operator, type
the name of the class followed by the < operator, the type of value, and the
> operator. Remember that, for a handle, you must use the ^ and the gcnew
operators. Here are two examples:
int main() { // Call the function and pass it an integer value Description<L'i'> iDscr; iDscr.Show(); // Call the function and pass it a character value Description<L'c'> ^ cDscr = gcnew Description<L'c'>; cDscr->Show(); // Call the function and pass it a double-precision value Description<L'f'> ^ dDscr = gcnew Description<L'f'>(L'f'); dDscr->Show(); return 0; }
A Class Template of an Unknown Type
|
As done for a function, you can create a template without
specifying the type of value it would hold. You can declare a member variable of
an unknown type. You can pass an unknown type to the methods of a class. To
create such a class template, you can use the following formula:
template <class Type> Options class ClassName { }
Once again, the template and class keywords
are required, so are the < and the > operators. Inside the <>, after
the class keyword, enter a name for the template. The Options factor can
be an appropriate combination of public, private, ref, and value keywords. After
the name of the class, represented in our formula as ClassName, delimit
the body of the class with the curly brackets. In the body of the class, you can
do whatever you judge necessary. You can declare a member variable as a template
type. You can pass an argument as a template type to one or more of the methods.
Here is an example of a class:
template <class TypeOfValue> public ref class Description { public: Description(); Description(TypeOfValue type); ~Description(); void Show(); String ^ Message; TypeOfValue Value; };
If you define a method in the body of the class, you can use
the member variable as you see fit. Here is an example:
template <class TypeOfValue>
public ref class Description
{
public:
Description();
Description(TypeOfValue type) : Value(type)
{
}
~Description();
void Show();
String ^ Message;
TypeOfValue Value;
};
As mentioned earlier, if you want to define a method outside
of the body of the class, precede it with the declaration of
template <class Type>
After typing the name of the class and before the ::
operator, enclose the template type within the < and > operators. Here is an
example:
template <class TypeOfValue> Description<TypeOfValue>::Description() { }
In both cases, whether defining the method in the body of
the class or outside, use the member variable. Here is an example:
template <class TypeOfValue>
void Description<TypeOfValue>::Show()
{
Console::WriteLine(L"{0}: {1}", Message, Value);
}
You can declare a variable of a class template the same way we
saw earlier, in the stack or in the managed heap. Here are examples:
using namespace System; template <class TypeOfValue> public ref class Description { public: Description(); Description(TypeOfValue type) : Value(type) { } ~Description(); void Show(); String ^ Message; TypeOfValue Value; }; template <class TypeOfValue> Description<TypeOfValue>::Description() { } template <class TypeOfValue> Description<TypeOfValue>::~Description() { } template <class TypeOfValue> void Description<TypeOfValue>::Show() { Console::WriteLine(L"{0}: {1}", Message, Value); } int main() { // Call the function and pass it an integer value // Declare the variable on the stack heap Description<int> iDscr; iDscr.Value = 246; iDscr.Message = L"Integer"; iDscr.Show(); // Call the function and pass it a character value // Declare the variable on the managed heap Description<__wchar_t> ^ cDscr = gcnew Description<__wchar_t>; cDscr->Value = 'G'; cDscr->Message = L"Character"; cDscr->Show(); // Call the function and pass it a double-precision value // Declare the variable on the managed heap Description<double> ^ dDscr = gcnew Description<double>(355.65); dDscr->Message = L"Decimal"; dDscr->Show(); return 0; }
|
- Access the ListOfItems.h header file and change it as follows:
#pragma once template <class T> public value struct CItem { T Item; CItem ^ Next; }; public ref class CListOfItems { private: int Size; public: // List Initialization CListOfItems(void); CItem<double> ^ Head; // List Finalization ~CListOfItems(); !CListOfItems(); // Operations on list int CListOfItems::Count(); int CListOfItems::Add(CItem<double> ^ item); CItem<double> ^ CListOfItems::Retrieve(int Position); };
- Access the ListOfItems.cpp source file and change it as follows:
#include "ListOfItems.h" CListOfItems::CListOfItems(void) : Head(nullptr), Size(0) { } CListOfItems::~CListOfItems() { delete Head; } CListOfItems::!CListOfItems() { } int CListOfItems::Count() { return Size; } int CListOfItems::Add(CItem<double> ^ NewItem) { CItem<double> ^ Sample = gcnew CItem<double>; Sample = NewItem; Sample->Next = Head; Head = Sample; return Size++; } CItem<double> ^ CListOfItems::Retrieve(int Position) { CItem<double> ^ Current = Head; for(int i = Count() - 1; i > Position && Current != nullptr; i--) { Current = Current->Next; } return Current; }
- Access the Exercise.cpp source file and change it as follows:
#include "ListOfItems.h" using namespace System; int main() { CItem<double> ^ item; CListOfItems ^ Items = gcnew CListOfItems; item = gcnew CItem<double>; item->Item = 97.46; Items->Add(item); item = gcnew CItem<double>; item->Item = 8844.82; Items->Add(item); item = gcnew CItem<double>; item->Item = 108.7873; Items->Add(item); item = gcnew CItem<double>; item->Item = 2764.48; Items->Add(item); item = gcnew CItem<double>; item->Item = 70062.83; Items->Add(item); Console::WriteLine(L"-=- List of Items -=-"); for(int i = 0; i < Items->Count(); i++) { CItem<double> ^ One = Items->Retrieve(i); Console::WriteLine(L"Item: {0}", One->Item); } Console::WriteLine(L"\nNumber of Items: {0}\n", Items->Count()); return 0; }
- Execute the application to see the result:
-=- List of Items -=- Item: 97.46 Item: 8844.82 Item: 108.7873 Item: 2764.48 Item: 70062.83 Number of Items: 5 Press any key to continue . . .
- Close the DOS window
We have seen that a template could be created using the class
keyword. As an alternative, you can use the typename keyword instead of class. Here is an example:
using namespace System; template <typename TypeOfValue> public ref class Description { public: Description(); Description(TypeOfValue type) : Value(type) { } ~Description(); void Show(); String ^ Message; TypeOfValue Value; }; template <typename TypeOfValue> Description<TypeOfValue>::Description() { } template <typename TypeOfValue> Description<TypeOfValue>::~Description() { } template <typename TypeOfValue> void Description<TypeOfValue>::Show() { Console::WriteLine(L"{0}: {1}", Message, Value); } int main() { // Call the function and pass it an integer value // Declare the variable on the stack heap Description<int> iDscr; iDscr.Value = 246; iDscr.Message = L"Integer"; iDscr.Show(); // Call the function and pass it a character value // Declare the variable on the managed heap Description<__wchar_t> ^ cDscr = gcnew Description<__wchar_t>; cDscr->Value = 'G'; cDscr->Message = L"Character"; cDscr->Show(); // Call the function and pass it a double-precision value // Declare the variable on the managed heap Description<double> ^ dDscr = gcnew Description<double>(355.65); dDscr->Message = L"Decimal"; dDscr->Show(); return 0; }
Based on this, the class keyword used for a template
creation can be replaced with typename.
A Class Template With Multiple Parameters
|
Using Multiple Templates Types
|
When creating a template, you can specify more than one
type. To do this, in the <> delimiters, after the first template type,
enter a comma and declare an identifier that has a type and a name. Here is an
example:
template <class TypeOfValue, __wchar_t c>
public ref class Description
{
public:
Description();
Description(TypeOfValue type) : Value(type)
{
}
~Description();
void Show();
String ^ Message;
TypeOfValue Value;
};
In this case, the second type must be, and will be treated
as, a constant. When creating the body of a method outside of the class, make
sure you specify the second type. Also, in the operators that follow the name of
the class, enter the name of the second type. Here is an example:
template <class TypeOfValue, __wchar_t c> Description<TypeOfValue, c>::Description() { }
When implementing the method, use the second argument as you
judge necessary. This should be easy because you know exactly its type. You can
display its value or you can involve it in an operation of your choice. In the
following example, the second type carries an indication of the message to be
displayed. To identified that message, the value of the second argument is
evaluated by a switch conditional statement:
template <class TypeOfValue, __wchar_t c> void Description<TypeOfValue, c>::Show() { switch(c) { case L'i': case L'I': Console::Write(L"Integer: "); break; case L'c': case L'C': Console::Write(L"Character: "); break; case L'f': case L'F': case L'd': case L'D': Console::Write(L"Decimal: "); break; } Console::WriteLine(L"{0}", Value); }
When instantiating the class, you must specify the second
type also. To do this, after the first type and its comma in the < and >
operators, enter a constant value for the second type. Here are examples:
int main() { // Call the function and pass it an integer value // Declare the variable on the stack heap Description<int, L'i'> iDscr; iDscr.Value = 246; iDscr.Message = L'i'; iDscr.Show(); // Call the function and pass it a character value // Declare the variable on the managed heap Description<__wchar_t, L'c'> ^ cDscr = gcnew Description<__wchar_t, L'c'>; cDscr->Value = 'G'; cDscr->Message = L'c'; cDscr->Show(); // Call the function and pass it a double-precision value // Declare the variable on the managed heap Description<double, L'f'> ^ dDscr = gcnew Description<double, L'f'>(355.65, L'f'); dDscr->Message = L'f'; dDscr->Show(); return 0; }
A Constant Template Parameter
|
Once again, remember that the second parameter is treated as
a constant; therefore, it can be declared using the const keyword. Here is an
example:
using namespace System; template <class TypeOfValue, const __wchar_t c> public ref class Description { public: Description(); Description(TypeOfValue type, const __wchar_t msg) : Value(type), Message(msg) { } ~Description(); void Show(); __wchar_t Message; TypeOfValue Value; }; template <class TypeOfValue, const __wchar_t c> Description<TypeOfValue, c>::Description() { } template <class TypeOfValue, const __wchar_t c> Description<TypeOfValue, c>::~Description() { Message = L'i'; } template <class TypeOfValue, const __wchar_t c> void Description<TypeOfValue, c>::Show() { switch(c) { case L'i': case L'I': Console::Write(L"Integer: "); break; case L'c': case L'C': Console::Write(L"Character: "); break; case L'f': case L'F': case L'd': case L'D': Console::Write(L"Decimal: "); break; } Console::WriteLine(L"{0}", Value); }
A Default Value for a Template Parameter
|
The second parameter of a class template can be assigned a
default value. Based on the rules of a default value, this value would be used
for the parameter if you don't supply a value for it. The default value can be
assigned only in the declaration preceding the class creation. Here is an
example:
using namespace System;
template <class TypeOfValue, const __wchar_t c = L'i'>
public ref class Description
{
public:
Description();
Description(TypeOfValue type, const __wchar_t msg)
: Value(type), Message(msg)
{
}
~Description();
void Show();
__wchar_t Message;
TypeOfValue Value;
};
template <class TypeOfValue, const __wchar_t c>
Description<TypeOfValue, c>::Description()
{
}
template <class TypeOfValue, const __wchar_t c>
Description<TypeOfValue, c>::~Description()
{
}
template <class TypeOfValue, const __wchar_t c>
void Description<TypeOfValue, c>::Show()
{
switch(c)
{
case L'i':
case L'I':
Console::Write(L"Integer: ");
break;
case L'c':
case L'C':
Console::Write(L"Character: ");
break;
case L'f':
case L'F':
case L'd':
case L'D':
Console::Write(L"Decimal: ");
break;
}
Console::WriteLine(L"{0}", Value);
}
When declaring a variable for the class, you can omit the
second parameter if you want. In this case, the default value would be used.
Here are two examples:
int main() { // Call the function and pass it an integer value Description<int, L'i'> iDscr; iDscr.Value = 246; iDscr.Message = L'i'; iDscr.Show(); // Notice that the second argument is not specified // This means that the default value will be used Description<__wchar_t> ^ cDscr = gcnew Description<__wchar_t>; cDscr->Value = 'G'; cDscr->Message = L'c'; cDscr->Show(); // Once again, notice that the second argument is not specified // The default value will be used for the parameter Description<double> ^ dDscr = gcnew Description<double>(355.65, L'f'); dDscr->Message = L'f'; dDscr->Show(); return 0; }
This would produce:
Integer: 246 Integer: G Integer: 355.65 Press any key to continue . . .
If you predict that the value of the first parameter can be
assigned to the second one, you can use that first parameter's value and assign
it to the second to be its default value.
A Class Template as a Template Parameter
|
When creating a class template, the first parameter can
itself be a class template. To create such a class, inside the <>
expression, include the type of declaration we have been using so far preceding
a class creation. This expression itself becomes template parameter and can be
used to precede the class keyword that is used to create a class template. Here
is an example of such a class:
template <template <class SomeType> class ValueType>
public ref class Homework
{
};
Of course, a class must have been previously created so it
can be used as a parameter. Here is an example:
template <class TypeOfValue> public ref class Description { }; template <template <class SomeType> class ValueType> public ref class Homework { };
Notice that both classes have nothing in common and they are
not sharing any information.
No comments:
Post a Comment