15 Templates PDF
15 Templates PDF
15. Templates
int main() {
return 0;
}
int main() {
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;
}
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;
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;
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() {
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() {
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() {
return 0;
}
int main() {
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() {
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() {
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)
int main() {
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!
int main() {
return 0; Output
} 6.2
6.4
3.4