The following code is a non-template version of our custom sort() function:
void sort ( array<int,6> data ) {
cout << "Non-template sort function invoked ..." << endl;
int size = data.size();
for ( int i=0; i<size; ++i ) {
for ( int j=0; j<size; ++j ) {
if ( data[i] < data[j] )
swap ( data[i], data[j] );
}
}
}
Non-template functions and template functions can coexist and participate in function overloading. One weird behavior of the preceding function is that the size of the array is hardcoded.
The second version of our sort() function is a template function, as shown in the following code snippet. Interestingly, the weird issue that we noticed in the first non-template sort() version is addressed here:
template <typename T, int size>
void sort ( array<T, size> data ) {
cout << "Template sort function invoked with one argument..." << endl;
for ( int i=0; i<size; ++i ) {
for ( int j=0; j<size; ++j ) {
if ( data[i] < data[j] )
swap ( data[i], data[j] );
}
}
}
In the preceding code, both the data type and the size of the array are passed as template arguments, which are then passed to the function call arguments. This approach makes the function generic, as this function can be instantiated for any data type.
The third version of our custom sort() function is also a template function, as shown in the following code snippet:
template <typename T>
void sort ( T data[], int size ) {
cout << "Template sort function invoked with two argument..." << endl;
for ( int i=0; i<size; ++i ) {
for ( int j=0; j<size; ++j ) {
if ( data[i] < data[j] )
swap ( data[i], data[j] );
}
}
}
The preceding template function takes a C-style array; hence, it also expects the user to indicate its size. However, the size of the array could be computed within the function, but for demonstration purposes, I need a function that takes two arguments. The previous function isn't recommended, as it uses a C-style array; ideally, we would use one of the STL containers.
Now, let's understand the main function code. The following code declares and initializes the STL array container with six values, which is then passed to our sort() function defined in the default namespace:
//Will invoke the non-template sort function
array<int, 6> a = { 10, 50, 40, 30, 60, 20 };
::sort ( a );
The preceding code will invoke the non-template sort() function. An important point to note is that, whenever C++ encounters a function call, it first looks for a non-template version; if C++ finds a matching non-template function version, its search for the correct function definition ends there. If the C++ compiler isn't able to identify a non-template function definition that matches the function call signature, then it starts looking for any template function that could support the function call and instantiates a specialized function for the data type required.
Let's understand the following code:
//Will invoke the template function that takes a single argument
array<float,6> b = { 10.6f, 57.9f, 80.7f, 35.1f, 69.3f, 20.0f };
::sort<float,6>( b );
This will invoke the template function that receives a single argument. As there is no non-template sort() function that receives an array<float,6> data type, the C++ compiler will instantiate such a function out of our user-defined sort() template function with a single argument that takes array<float, 6>.
In the same way, the following code triggers the compiler to instantiate a double version of the template sort() function that receives array<double, 6>:
//Will invoke the template function that takes a single argument
array<double,6> c = { 10.6d, 57.9d, 80.7d, 35.1d, 69.3d, 20.0d };
::sort<double,6> ( c );
Finally, the following code will instantiate an instance of the template sort() that receives two arguments and invokes the function:
//Will invoke the template function that takes two arguments
double d[] = { 10.5d, 12.1d, 5.56d, 1.31d, 81.5d, 12.86d };
::sort<double> ( d, 6 );
If you have come this far, I'm sure you like the C++ template topics discussed so far.