Templates in C++ (original) (raw)

Try it on GfG Practice redirect icon

C++ **template is a powerful tool that allows you to write a generic code that can work with any data type. The idea is to simply pass the data type as a parameter so that we don't need to write the same code for different data types.

For example, same sorting algorithm can work for different type, so rather than writing and maintaining multiple codes, we can write one sort() and pass the datatype as a parameter.

Define Templates

Templates can be defined using the keywords "**template" and "**typename" as shown:

C++ `

template <typename A, typename B, ...> entity_definition

`

The **template keyword is used to define that the given entity is a template and **typename keyword is used to define **template parameters which are nothing but types that will be provided when an instance is created. The keyword **typename can be replaced by keyword **class anytime.

C++ `

template <class A, class B, ...> entity_definition

`

The above syntax can define templates for three components (entities) of C++ language:

Create Template Instance

After definition, we can create the instance of template for any desired type by passing the type as template parameter as shown:

C++ `

name_of_entity<type1, type2, ...>

`

The **type1 will be substituted by the **typename A in the definition, **type2 will be substituted in place of **typename B and so on.

**Function Templates

In C++, templates allow us to write **generic code for functions that can be used with different data types, and this can be achieved by function templates. **For example, we can write a function that gives you the maximum of two numbers, but it can accept any number whether it is int, float, or double.

C++ `

#include using namespace std;

// Function template definition template T myMax(T x, T y) { return (x > y) ? x : y; }

int main() {

// Call myMax for int
cout << myMax<int>(3, 7) << endl;

// call myMax for double
cout << myMax<double>(3.0, 7.0) << endl;

// call myMax for char
cout << myMax<char>('g', 'e');

return 0;

}

`

Class Templates

Class templates like function templates, are useful when a class defines something that is independent of the data type. It is useful for classes like LinkedList, BinaryTree, Stack, Queue, Array, etc.

**Example:

C++ `

#include using namespace std;

// Defining class template template class Geek { public: T x; T y;

// Constructor
Geek(T val1, T val2) : x(val1), y(val2) {}

// Method to get values
void getValues() {
    cout << x << " " << y;
}

};

int main() {

// Creating objects of Geek with
// different data types
Geek<int> intGeek(10, 20);
Geek<double> doubleGeek(3.14, 6.28);

// Access the templates values
intGeek.getValues();
cout << endl;
doubleGeek.getValues();

return 0;

}

`

We can pass more than one data type as arguments to templates.

**Example:

C++ `

#include using namespace std;

// Defining class template with // multiple type parameters template <typename T1, typename T2, typename T3> class Geek { public: T1 x; T2 y; T3 z;

// Constructor to initialization
Geek(T1 val1, T2 val2, T3 val3) :
    x(val1), y(val2), z(val3) {}

// Method to get values
void getValues() {
    cout << x << " " << y << " " << z;
}

};

int main() {

// Creating objects of Geek
// with different data types
Geek<int, double, string> intDoubleStringGeek(10, 3.14, "Hello");
Geek<char, float, bool> charFloatBoolGeek('A', 5.67f, true);

intDoubleStringGeek.getValues();
cout << endl;
charFloatBoolGeek.getValues();

return 0;

}

`

Output

10 3.14 Hello A 5.67 1

Template Variables (Since C++ 14)

A template variable is a variable that can work with any type specified when the variable is used, similar to how we use templates for functions or classes.

**Syntax:

C++ `

template constexpr T pi = T(3.14159);

`

In the above statement, pi is the template variable. We use constexpr with the template variable because it ensures that the variable is a constant expression and is evaluated at compile time rather than at runtime.

**Example:

C++ `

#include using namespace std;

// Template variable with constexpr template constexpr T pi = T(3.14159);

int main() { // Using pi with different types cout << "Pi as float: " << pi << endl; cout << "Pi as double: " << pi; return 0; }

`

Output

Pi as float: 3.14159 Pi as double: 3.14159

Default Template Arguments

Like normal parameters, we can also specify default type arguments to templates.

**Example:

C++ `

#include using namespace std;

// Defining class template with // multiple type parameters and one default type template <typename T1, typename T2 = double, typename T3 = string> class Geek { public: T1 x; T2 y; T3 z;

// Constructor to initialize data members
Geek(T1 val1, T2 val2, T3 val3) :
x(val1), y(val2), z(val3) { }

// Method to get values
void getValues() {
    cout << x << " " << y << " " << z;
}

};

int main() {

// Creating objects of Geek
// with different data types
Geek<int, float, string> intFloatStringGeek(10, 5.67f, "Hello");
Geek<char> charDoubleStringGeek('A', 3.14, "World");

intFloatStringGeek.getValues();
cout << endl;
charDoubleStringGeek.getValues();

return 0;

}

`

Output

10 5.67 Hello A 3.14 World

Working of Templates

Templates are expanded at compiler time. This is like macros. The difference is that the compiler does type-checking before template expansion. The idea is simple, source code contains only function/class, but compiled code may contain multiple copies of the same function/class.

Template Specialization

In C++, **template specialization allows us to define different implementations of a template for specific data types or combinations of data types.

**Example:

C++ `

#include <bits/stdc++.h> using namespace std;

// Generic template template void print(T value) { cout << value; }

// Template specialization for int template <> void print(int value) { cout << "Value: " << value; }

int main() { print(3); return 0; }

`

**Template Non-Type Arguments

We can pass **non-type arguments to templates. Non-type parameters are mainly used for specifying max or min values or any other constant value for a particular instance of a template. The important thing to note about non-type parameters is, that they must be const. The compiler must know the value of non-type parameters at compile time. Because the compiler needs to create functions/classes for a specified non-type of value at compile time.

**Example:

C++ `

#include using namespace std;

// Second argument of template is // not template type template <class T, int max> int arrMin(T arr[], int n) { int m = max; for (int i = 0; i < n; i++) if (arr[i] < m) m = arr[i]; return m; }

int main() {

int arr1[] = {10, 20, 15, 12};
int n1 = sizeof(arr1) / sizeof(arr1[0]);
char arr2[] = {1, 2, 3};
int n2 = sizeof(arr2) / sizeof(arr2[0]);

// Second template parameter
// to arrMin must be a
// constant
cout << arrMin<int, 10000>(arr1, n1) << endl;
cout << arrMin<char, 256>(arr2, n2);

return 0;

}

`

Template Argument Deduction

Template argument deduction automatically deduces the data type of the argument passed to the templates. This allows us to instantiate the template without explicitly specifying the data type.

**Note: It is important to note that the template argument deduction for classes is only available since C++17, so if we try to use the auto template argument deduction for a class in previous version, it will throw an error.

**Example:

The below example demonstrates how the STL **max() method deduces the data type without being explicitly specified.

C++ `

#include <bits/stdc++.h> using namespace std;

int main() { cout << max(3, 4); return 0; }

`

**Note: The above program will fail compilation in C++14 and below compiler since class template arguments deduction was added in C++17.

Function Template Arguments Deduction

Function template argument deduction has been part of C++ since the C++98 standard. We can skip declaring the type of arguments we want to pass to the function template and the compiler will automatically deduce the type using the arguments we passed in the function call.

**Example:

C++ `

#include using namespace std;

template t multiply(t first, t second) { return first * second; }

int main() { cout << multiply(3, 4); return 0; }

`

**Note: For the function templates which is having the same type for the arguments like template void function(t a1, t a2){}, we cannot pass arguments of different types.

Class Template Arguments Deduction (C++17 Onwards)

The class template argument deduction was added in C++17 and has since been part of the language. It allows us to create the class template instances without explicitly definition the types just like function templates.

**Example:

C++ `

#include using namespace std;

template class Geek { public: T x; T y; Geek(T val1, T val2) : x(val1), y(val2) { } void getValues() { cout << x << " " << y; } };

int main() {

// Class template argument deduction
Geek intGeek(10, 20);
Geek doubleGeek(3.14, 6.28);
intGeek.getValues();
cout << endl;
doubleGeek.getValues();
return 0;

}

`

**Output

10 20
3.14 6.28

In C++, **template metaprogramming refers to template perform computation at the compile time rather than runtime. To perform computation at compile time, template metaprogramming involves recursive template structures where templates call other templates during compilation.

**Example:

C++ `

#include using namespace std;

// Template metaprogramming for // calculating factorial at compile-time template struct Factorial { static const int value = N * Factorial<N - 1>::value; };

// Specialization for the // base case (Factorial<0>) template <> struct Factorial<0> { static const int value = 1; };

int main() { // Factorial computation // happens at compile-time cout << "Factorial of 5 is: " << Factorial<5>::value; return 0; }

`

Output

Factorial of 5 is: 120

Variadic Templates

If you notice carefully, the number of parameters in regular templates is fixed. However, with **variadic templates, we can pass any number of parameters to the templates.

**Example:

C++ `

#include using namespace std;

// Base case: when no // arguments are left int sum() { return 0; }

// Recursive case: sum the // first argument and recursively // sum the rest template <typename T, typename... Args> T sum(T first, Args... args) {

// Recursive call with
// remaining argument
return first + sum(args...);

}

int main() { cout << "Sum of 1, 2, 3: " << sum(1, 2, 3) << endl; cout << "Sum of 4, 5: " << sum(4, 5); return 0; }

`

Output

Sum of 1, 2, 3: 6 Sum of 4, 5: 9

Templates vs Function Overloading

Differences **between templates and function overloading are shown below:

**Function Overloading **Function Templates
Function overloading allows multiple functions with the same name but different parameters (number or types). Function templates define a generic function that works with any data type.
Different functions with the same name must have different parameter types, number of parameters, or both. A single template function can work with any data type by using type parameters.
Each overloaded function is explicitly defined for specific types. A function template is defined once and can be used for multiple types.
Requires writing separate functions for each type. No code duplication, as the same template is used for various types.
Used when you need to perform the same task on different data types, but with different implementations. Used when the same function logic applies to different types, and type is deduced or explicitly provided.