Function Template Overloading
We can declare several function templates with the same name and even declare a combination of function templates and ordinary functions with the same name.
The compiler needs to perform overload resolution to determine the correct function or template function to invoke.
Overloading resolution is done as follows:
- Find the set of function template specializations that will take part in overload resolution, by considering each function template and deciding which template arguments, if any, would be used if no other function templates or functions of the same name were in scope.
- If two template functions can be called and one is more specialized than the other, consider only the most specialized template function
- Do overload resolution for this set of functions, plus any ordinary functions, as for ordinary functions. If a template function argument has been determined by template argument declaration, that argument cannot have promotions, standard conversions, or user-defined conversions applied.
- If a function and a specialization are equally good matches, the function is preferred.
- If no match is found, the call is in error.
If there are two or more equally good matches, the call is ambiguous.
For example:
//
// main.cpp
// Function Overload Resolution
//
// Created by Bryan Higgs on 10/13/24.
//
#include <iostream>
template <typename T>
T max(T first, T second)
{
std::cout << "first : " << typeid(first).name() << " "
<< "value: " << first << std::endl;
std::cout << "second : " << typeid(second).name() << " "
<< "value: " << second << std::endl;
T maxValue = first > second ? first : second;
std::cout << "Max: " << maxValue << std::endl;
return maxValue;
}
int main(int argc, const char * argv[])
{
const int s = 7;
max(1, 2); // max<int>(1, 2)
max('a', 'b'); // max<char>('a', 'b')
max(2.7, 4.9); // max<double>(2.7, 4.9)
max(s, 7); // max<int>(int(s), 7)
max('a', 1); // error: ambiguous
max(2.7, 4); // error: ambiguous
return 0;
}which produces the following compile-time errors:
main.cpp:31:3 No matching function for call to 'max'
Candidate template ignored: deduced conflicting types for parameter 'T' ('char' vs. 'int')
main.cpp:32:3 No matching function for call to 'max'
Candidate template ignored: deduced conflicting types for parameter 'T' ('double' vs. 'int')
When I remove the offending statements:
//
// main.cpp
// Function Overload Resolution
//
// Created by Bryan Higgs on 10/13/24.
//
#include <iostream>
template <typename T>
T max(T first, T second)
{
std::cout << "first : " << typeid(first).name() << " "
<< "value: " << first << std::endl;
std::cout << "second : " << typeid(second).name() << " "
<< "value: " << second << std::endl;
T maxValue = first > second ? first : second;
std::cout << "Max: " << maxValue << std::endl;
return maxValue;
}
int main(int argc, const char * argv[])
{
const int s = 7;
max(1, 2); // max<int>(1, 2)
max('a', 'b'); // max<char>('a', 'b')
max(2.7, 4.9); // max<double>(2.7, 4.9)
max(s, 7); // max<int>(int(s), 7)
return 0;
}the program produces the following output:
first : i value: 1
second : i value: 2
Max: 2
first : c value: a
second : c value: b
Max: b
first : d value: 2.7
second : d value: 4.9
Max: 4.9
first : i value: 7
second : i value: 7
Max: 7
Program ended with exit code: 0
(See above for mangled type meanings.)
Function Template Overload Resolution
The ambiguities can be resolved in two ways:
- By using explicit qualification, or:
- By adding suitable declarations
Using explicit qualification:
//
// Resolution1.cpp
// Function Overload Resolution
//
// Created by Bryan Higgs on 10/13/24.
//
#include <iostream>
template <typename T>
T max(T first, T second)
{
std::cout << "first : " << typeid(first).name() << " "
<< "value: " << first << std::endl;
std::cout << "second : " << typeid(second).name() << " "
<< "value: " << second << std::endl;
T maxValue = first > second ? first : second;
std::cout << "Max: " << maxValue << std::endl;
return maxValue;
}
int main(int argc, const char * argv[])
{
//max('a', 1); // error: ambiguous
max<int>('a', 1); // max<int>(int('a'), 1)
//max(2.7, 4); // error: ambiguous
max<double>(2.7, 4); // max<double>(2.7, double(4))
return 0;
}This program produces the following output:
first : i value: 97
second : i value: 1
Max: 97
first : d value: 2.7
second : d value: 4
Max: 4
Program ended with exit code: 0
(See above for mangled type meanings.)
Adding suitable declarations:
//
// Resolution2.cpp
// Function Overload Resolution
//
// Created by Bryan Higgs on 10/13/24.
//
#include <iostream>
template <typename T>
T max(T first, T second)
{
std::cout << "first : " << typeid(first).name() << " "
<< "value: " << first << std::endl;
std::cout << "second : " << typeid(second).name() << " "
<< "value: " << second << std::endl;
T maxValue = first > second ? first : second;
std::cout << "Max: " << maxValue << std::endl;
return maxValue;
}
inline int max(char c, int i)
{ return max<int>(c, i); }
inline int max(int i, char c)
{ return max<int>(i, c); }
inline int max(int i, int j)
{ return max<int>(i,j); }
inline double max(int i, double d)
{ return max<double>(i, d); }
inline double max(double d, int i)
{ return max<double>(d, i); }
inline double max(double d1, double d2)
{ return max<double>(d1, d2); }
int main(int argc, const char * argv[])
{
max('a', 1); // max<int>(int('a'), 1)
max(2.7, 4); // max<double>(2.7, double(4))
return 0;
}This program also produces the following output:
first : i value: 97
second : i value: 1
Max: 97
first : d value: 2.7
second : d value: 4
Max: 4
Program ended with exit code: 0
(See earlier for mangled type meanings.)
But it kinda defeats the purpose of using templates, right?