0% found this document useful (0 votes)
86 views27 pages

15 Templates PDF

The document discusses templates in C++. It provides examples of using templates to create generic functions that can operate on different data types. The key points covered include: - Templates allow defining functions that work on different data types through a common interface. - Template arguments can be deduced automatically based on function parameters, or specified explicitly. - Template functions and non-template functions can be overloaded if they have the same name. - Template functions provide a way to consolidate code and avoid duplication when defining functions that perform the same operations on different types.

Uploaded by

Utsav Vedant
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
86 views27 pages

15 Templates PDF

The document discusses templates in C++. It provides examples of using templates to create generic functions that can operate on different data types. The key points covered include: - Templates allow defining functions that work on different data types through a common interface. - Template arguments can be deduced automatically based on function parameters, or specified explicitly. - Template functions and non-template functions can be overloaded if they have the same name. - Template functions provide a way to consolidate code and avoid duplication when defining functions that perform the same operations on different types.

Uploaded by

Utsav Vedant
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 27

Object Oriented Programming with C++

15. Templates

By: Prof. Pandav Patel

Third Semester, September 2020


Computer Engineering Department
Dharmsinh Desai University
int product(int num1, int num2) { Output
return num1 * num2; 6
} 2.42
2.42
float product(float num1, float num2) {
return num1 * num2;
}

double product(double num1, double num2) {


return num1 * num2;
}

int main() {

cout << product(2, 3) << endl;


cout << product(1.1f, 2.2f) << endl;
cout << product(1.1, 2.2) << endl;

return 0;
}

Notice duplication of code in all three variants of product function


Is there a way to consolidate these different functions? As what they are doing remains same but only
thing that differs is the type they handle
template<typename T> Output
T product(T num1, T num2) { 6
return num1 * num2; 2.42
} 2.42

int main() {

cout << product(2, 3) << endl;


cout << product(1.1f, 2.2f) << endl;
cout << product(1.1, 2.2) << endl;

return 0;
}

Compiler generates actual code for function product (for types with which it has been called in the
program). Here compiler will instantiate three overloads of function product as we are calling it with int,
float and double types.
template<typename T> Output
T product(T num1, T num2) { 6
return num1 * num2; 2.42
} 12
int main() { 13.5
cout << product(2, 3) << endl;
cout << product(1.1f, 2.2f) << endl;
// error: no matching function for call to ‘product(int, double)
// note: deduced conflicting types for parameter ‘T’ (‘int’ and ‘double’)
// cout << product(3, 4.5) << endl;
cout << product<int>(3, 4.5) << endl;
cout << product<float>(3, 4.5) << endl;
// string s1 = "Hello ", s2 = "world!\n";
// error: no match for ‘operator*’
// cout << product(s1, s2) << endl;

return 0;
}

If template arguments are not explicitly specified during function call, then they are deduced by compiler
based on function arguments and function template parameters.

Compiler will raise an error if there is conflict in deduction
We may call function with explicit template arguments

e.g. product<int>(3, 4.5)

It will now consider T as int
Compiler will instantiate only two overloads of function product as we are calling it with int and float types.
template<typename T>
T product(T num1, T num2) {
return num1 * num2;
}

Is same as (atleast for our basic use)

template<class T>
T product(T num1, T num2) {
return num1 * num2;
}

Even when you use class keyword you can call this function with fundamental data types as T
So, prefer use of keyowrd typename over keyword class as it is close to its actual behaviour
template<typename T> Output
void swap(T &num1, T &num2) { Before swap: int1 = 5 and int2 = 7
T temp; After swap: int1 = 7 and int2 = 5
temp = num1; Before swap: double1 = 5 and double2 = 7
num1 = num2; After swap: double1 = 7 and double2 = 5
num2 = temp;
}
int main() {
int int1 = 5, int2 = 7;
cout << "Before swap: int1 = " << int1;
cout << " and int2 = " << int2 << endl;
swap(int1, int2);
cout << "After swap: int1 = " << int1;
cout << " and int2 = " << int2 << endl;

double double1 = 5, double2 = 7;


cout << "Before swap: double1 = " << double1;
cout << " and double2 = " << double2 << endl;
swap(double1, double2);
cout << "After swap: double1 = " << double1;
cout << " and double2 = " << double2 << endl;

return 0;
}
Just another example, Templates can have references or pointers of T as well
T is generally used as typename, but it could be any valid identifier
template<typename _abc_T> Output
void swap(_abc_T &num1, _abc_T &num2) { Before swap: int1 = 5 and int2 = 7
_abc_T temp; After swap: int1 = 7 and int2 = 5
temp = num1; Before swap: double1 = 5 and double2 = 7
num1 = num2; After swap: double1 = 7 and double2 = 5
num2 = temp;
}
int main() {
int int1 = 5, int2 = 7;
cout << "Before swap: int1 = " << int1;
cout << " and int2 = " << int2 << endl;
swap(int1, int2);
cout << "After swap: int1 = " << int1;
cout << " and int2 = " << int2 << endl;

double double1 = 5, double2 = 7;


cout << "Before swap: double1 = " << double1;
cout << " and double2 = " << double2 << endl;
swap(double1, double2);
cout << "After swap: double1 = " << double1;
cout << " and double2 = " << double2 << endl;

return 0;
}
Just another example, Templates can have references or pointers of T as well
T is generally used as typename, but it could be any valid identifier
template<typename T> Output
T product(T num1, T num2) { Template function called
cout << "Template function called\n"; 6
return num1 * num2; Non-template function called
} 2.42
float product(float num1, float num2) { Template function called
cout << "Non-template function called\n"; 2.42
return num1 * num2; Template function called
} 2.42
int product(int num1, int num2, int num3) { Template function called
cout << "Non-template function with three argumets called\n"; 2.42
return num1 * num2 * num3; Non-template function with three argumets called
} 6
int main() {
cout << product(2, 3) << endl; Function template and non-template function can have same
cout << product(1.1f, 2.2f) << endl;
name
cout << product<float>(1.1f, 2.2f) << endl; Overload resolution for function template and non-template
cout << product<>(1.1f, 2.2f) << endl;
function is as follows (when called without explicit template
cout << product(1.1, 2.2) << endl;
arguments)
// Not an exact match 
Call non-template function that has exact match
cout << product(1.1, 2.2, 3.3) << endl; 
Instantiate function from function template (if possible with
exact match)
return 0; 
Try overload resolution with non-template overloads of a
}
function
template<typename T> Output
T product(T num1, int num2, T num3) { Template function called
cout << "Template function called\n"; 18.75
return num1 * num2 * num3; Template function called
} 18.75

int main() {

cout << product(2.5, 3, 2.5) << endl;

cout << product(2.5, 3.5, 2.5) << endl;

// error: no matching function for call to ‘product(double, double, int)’


// note: deduced conflicting types for parameter ‘T’ (‘double’ and ‘int’)
// cout << product(2.5, 3.5, 2) << endl;

return 0;
} Function arguments can be mixed
Implicit conversion can take place for fixed type arguments of a function
template<typename T1, typename T2> Output
T2 product(T2 num1, T2 num2, T1 num3) { Template function called
cout << "Template function called\n"; 21.875
return num1 * num2 * num3; Template function called
} 17.5

int main() {

// error: no matching function for call to ‘product(double, int, double)’


// note: deduced conflicting types for parameter ‘T2’ (‘double’ and ‘int’)
// cout << product(2.5, 3, 2.5) << endl;

cout << product(2.5, 3.5, 2.5) << endl;

cout << product(2.5, 3.5, 2) << endl;

return 0;
} Function templates can have multiple template parameters (e.g. T1 and T2
in this case)
There is no correlation between sequence of template parameters and
sequence of function parameters.
template<typename T1, typename T2> Output
T1 product(T2 num1, T2 num2, T1 num3) { Template function called
cout << "Template function called\n"; 15
T1 result; Template function called
result = num1 * num2 * num3; 21.875
return result; Template function called
} 17

int main() {

cout << product<double,int>(2.5, 3, 2.5) << endl;

cout << product(2.5, 3.5, 2.5) << endl;

cout << product(2.5, 3.5, 2) << endl;

return 0;
}

Type specified in template parameters can also be used in function body


Template arguments can be specified during function call even when
template has multiple parameters
template<typename T1, typename T2>
T2 average(T1 arr[], int n) {
T2 result;
for(int i = 0; i < n; i++) {
result += arr[i];
}
result /= n;
return result;
}

int main() {

float arr[5] = {1.1, 2.2, 3.3, 4.4, 5.5};


// error: no matching function for call to ‘average(float [5], int)’
// couldn't deduce template parameter ‘T2’
cout << average(arr, 5);

return 0;
}
Here compiler is not able to deduce type of T2
Here are two different ways to solve it (there may be more)

Call average() function with template arguments

Remove T2 from list of template parameters and make result and
return type to be of fixed type
template<typename T1, typename T2> Output
T2 average(T1 arr[], int n) { 3.3
T2 result;
for(int i = 0; i < n; i++) {
result += arr[i];
}
result /= n;
return result;
}

int main() {

float arr[5] = {1.1, 2.2, 3.3, 4.4, 5.5};

cout << average<float, double>(arr, 5);

return 0;
}
Calling average() function with template arguments
template<typename T1> Output
double average(T1 arr[], int n) { 3.3
double result;
for(int i = 0; i < n; i++) {
result += arr[i];
}
result /= n;
return result;
}

int main() {

float arr[5] = {1.1, 2.2, 3.3, 4.4, 5.5};

cout << average(arr, 5);

return 0;
}
Removed T2 from list of template parameters and made result and return
type to be of double type
template<typename T1, typename T2> int main() {
T1 sum(T1 arg1, T2 arg2) {
return arg1 + arg2; cout << sum(1.2, 2) << endl;
} cout << sum("Hello ", 2) << endl;
// error: no match for ‘operator+’
class Complex { // cout << sum(string("Hello "), 2) << endl;
double re, im; cout << sum(string("Hello "), string("world!")) << endl;
public:
Complex(double re, double im) { Complex c1(1.1, 2.2), c2(3.3, 4.4);
this->re = re; cout << sum(c1, c2) << endl;
this->im = im;
} return 0;
Complex operator+(const Complex &c) { } Output
return Complex(re + c.re, im + c.im); 3.2
} llo
friend std::ostream &operator<<(std::ostream &, const Complex &); Hello world!
}; (4.4, 6.6)

std::ostream &operator<<(std::ostream &strm, const Complex &c) {


strm << "(" << c.re << ", " << c.im << ")\n";
return strm;
}
Two different parameters of a template (T1 and T2 here), can be deduced/specified to be of same type
We can pass user defined type (e.g. Complex in this example) as argument to template parameters
template<typename T1, typename T2>
T1 sum(T1 arg1, T2 arg2) {
return arg1 + arg2;
}

int main() {

cout << sum<int>(1.2, 2) << endl;


cout << sum(string("Hello "), string("world!")) << endl;

return 0;
}
Output
3
Hello world!

If only one type is passed as template argument then other types of remaining template parameters will be
deduced by compiler.
template<typename T1, typename T2>
T1 sum(T1 arg1, T2 arg2 = 5) {
return arg1 + arg2;
}

int main() {
// error: no matching function for call to ‘sum(double)’
// note: couldn't deduce template parameter ‘T2’
// cout << sum(1.2) << endl;
cout << sum<double, int>(1.2) << endl;
cout << sum(string("Hello "), string("world!")) << endl;
// could not convert ‘5’ from ‘int’ to ‘std::string’
// cout << sum<string, string>(string("Hello ")) << endl;

return 0;
} Output
6.2
Hello world!

Function parameters of function template can take default values


But if you call scuh function without passing actual argument (for default parameter), compiler will not deduce its
type based on default argument for function parameter. You need to explicitly specify template argument during
call
Here T2 can even be string type as far as you provide value for arg2 during function call
template<typename T1, typename T2 = int>
T1 sum(T1 arg1, T2 arg2 = 5.2) {
return arg1 + arg2;
}

int main() {

cout << sum(1.2) << endl;


cout << sum<double, double>(1.2) << endl;
cout << sum(1.2, 2.2) << endl;

return 0; Output
} 6.2
6.4
3.4

You can specify default type for the template parameter


If compiler can deduce the type for template parameter from fuction argument then it will ignore default type
Default value of function argument would not be used for deducing type for template parameter
class StackOverflowException: public std::exception { T pop() {
public: if(top == -1)
virtual const char* what() const throw() throw *(new StackUnderflowException);
{ return arr[top--];
return "StackOverflowException\n"; }
} ~Stack(){
}; delete [] arr;
class StackUnderflowException: public std::exception { }
public: };
virtual const char* what() const throw() int main()
{ {
return "StackUnderflowException\n"; Stack<int> int_stack(3);
} try {
}; int_stack.push(5);
template <typename T> int_stack.push(7); Output
class Stack int_stack.push(3); 3
{ cout << int_stack.pop() << endl;
T *arr; cout << int_stack.pop() << endl; 7
int top; int_stack.push(9); 9
int capacity; cout << int_stack.pop() << endl;
public: }
Stack(int size) { catch(std::exception &e) {
arr = new T[size]; cout << e.what();
capacity = size; }
top = -1; return 0;
} }
void push(T element) { An example of class template. template keyoword and parameters are
if(top == capacity - 1) needed before class (like function template)
throw *(new StackOverflowException);
arr[++top] = element;
Template keyword and parameter list is not needed for inline methods
} Template parameters can be used by all methods inside class
Should explicitly specify template arguments while creating object (Deduction possible since C++17 based on
constructor parameters and arguments while creating object).
class StackOverflowException: public std::exception { T pop() {
public: if(top == -1)
virtual const char* what() const throw() throw *(new StackUnderflowException);
{ return arr[top--];
return "StackOverflowException\n"; }
} ~Stack(){
}; delete [] arr;
class StackUnderflowException: public std::exception { }
public: };
virtual const char* what() const throw() int main()
{ {
return "StackUnderflowException\n"; Stack<string> str_stack(3);
} try {
}; str_stack.push(string("ABC"));
template <typename T> str_stack.push(string("PQR")); Output
class Stack str_stack.push(string("XYZ")); XYZ
{ cout << str_stack.pop() << endl;
T *arr; cout << str_stack.pop() << endl; PQR
int top; str_stack.push(string("LMN")); LMN
int capacity; cout << str_stack.pop() << endl;
public: }
Stack(int size) { catch(std::exception &e) {
arr = new T[size]; cout << e.what();
capacity = size; }
top = -1; return 0;
} }
void push(T element) { Creating stack of strings using the same class template as in previous
if(top == capacity - 1) slide
throw *(new StackOverflowException);
arr[++top] = element;
}
class StackOverflowException: public std::exception { T pop() {
public: if(top == -1)
virtual const char* what() const throw() throw *(new StackUnderflowException);
{ return arr[top--];
return "StackOverflowException\n"; }
} ~Stack(){
}; delete [] arr;
class StackUnderflowException: public std::exception { }
public: };
virtual const char* what() const throw() int main()
{ {
return "StackUnderflowException\n"; Stack<string> str_stack(3);
} try {
}; str_stack.push(string("ABC"));
template <typename T> str_stack.push(string("PQR")); Output
class Stack str_stack.push(string("XYZ")); StackOverflowException
{ str_stack.push(string("LMN"));
T *arr; cout << str_stack.pop() << endl;
int top; cout << str_stack.pop() << endl;
int capacity; cout << str_stack.pop() << endl;
public: }
Stack(int size) { catch(std::exception &e) {
arr = new T[size]; cout << e.what();
capacity = size; }
top = -1; return 0;
} }
void push(T element) {
if(top == capacity - 1)
throw *(new StackOverflowException);
arr[++top] = element;
}
class StackOverflowException: public std::exception { template <typename T>
public: void Stack<T>::push(T element) {
virtual const char* what() const throw() if(top == capacity - 1)
{ throw *(new StackOverflowException);
return "StackOverflowException\n"; arr[++top] = element;
} }
}; template <typename T>
class StackUnderflowException: public std::exception { T Stack<T>::pop() {
public: if(top == -1)
virtual const char* what() const throw() throw *(new StackUnderflowException);
{ return arr[top--];
return "StackUnderflowException\n"; }
} int main()
}; {
template <typename T> Stack<string> str_stack(3); Output
class Stack try { XYZ
{ str_stack.push(string("ABC")); LMN
T *arr; str_stack.push(string("PQR")); PQR
int top; str_stack.push(string("XYZ"));
int capacity; cout << str_stack.pop() << endl;
public: str_stack.push(string("LMN"));
Stack(int size); cout << str_stack.pop() << endl;
void push(T element); cout << str_stack.pop() << endl;
T pop(); }
~Stack(){ catch(std::exception &e) {
delete [] arr; cout << e.what();
} }
}; return 0;
template <typename T> }
Stack<T>::Stack(int size) {
Methods of a class template can also be defined outside the class definition.
arr = new T[size];
capacity = size; When methods aredefined outside class definition then they need to have template
top = -1; parameters and specification that it is a method of template class (e.g. Stack<T>)
}
class StackOverflowException: public std::exception { T pop() {
public: if(top == -1)
virtual const char* what() const throw() throw *(new StackUnderflowException);
{ return arr[top--];
return "StackOverflowException\n"; }
} ~Stack(){
}; delete [] arr;
class StackUnderflowException: public std::exception { }
public: };
virtual const char* what() const throw() int main()
{ {
return "StackUnderflowException\n"; // int size;
} // cin >> size;
}; // error: the value of ‘size’ is not usable in a constant expression
template <typename T, int size> // in this case, size must be known to the compiler at compile time
class Stack // Stack<string, size> str_stack;
{ Stack<string, 3> str_stack;
T *arr; try { Output
int top; str_stack.push(string("ABC")); XYZ
int capacity; str_stack.push(string("PQR")); PQR
public: str_stack.push(string("XYZ"));
Stack() { cout << str_stack.pop() << endl; LMN
arr = new T[size]; cout << str_stack.pop() << endl;
capacity = size; str_stack.push(string("LMN"));
top = -1; cout << str_stack.pop() << endl;
} }
void push(T element) { catch(std::exception &e) {
if(top == capacity - 1) cout << e.what();
throw *(new StackOverflowException); }
arr[++top] = element; return 0;
} }
Class template can have non-type parameters (e.g. int size in this case). Size is not a
type (typename or class). Its int type. But its value should be known at compile time.
●Function template and class template can have more than one non-
type parameters, type parameters or template parameters
●Type deduction is possible for class template since C++17, based on
types of arguments passed while creating object and types of
constructor parameters

https://en.cppreference.com/w/cpp/language/class_template_argument_deduction
●You may overload a function template either by a non-template function
or by another function template.

https://stackoverflow.com/questions/2174300/function-template-overloading
Interesting reads

Function Template

https://en.cppreference.com/w/cpp/language/function_template

Class Template

https://en.cppreference.com/w/cpp/language/class_template

Class template argument deduction (CTAD) (since C++17)

https://en.cppreference.com/w/cpp/language/class_template_argument_deduction

You may overload a function template either by a non-template function or by another function template.

https://stackoverflow.com/questions/2174300/function-template-overloading

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy