Functions that do roughly similar work sometimes need to accept different data types. Duplicating a name for each version can get very cumbersome very quickly.
Function overloading is the feature which allows many functions to have the same name but with different argument lists.
The compiler recognizes each unique set of parameters as a different function. The function name plus the number and type of parameters determine which combination is called.
The return type is not included in that identity. Two functions which differ only by their return type are not a valid overload.
#include <iostream>
void show(int value)
{
std::cout << "Integer version: " << value << '\n';
}
void show(double value)
{
std::cout << "Double version: " << value << '\n';
}
void show(int a, int b)
{
std::cout << "Two integers: " << a << " and " << b << '\n';
}
When the function is called, the compiler compares the arguments that are passed in and finds the most suitable overload.
A call with one integer will choose the version which takes one int. A call with one floating point value can choose the version which takes a double. A call with two integers chooses the version with two ints.
Each call is resolved during compiling the program.
The finished program has additional functions that for some reason have the same understandable names.
int main()
{
show(10);
show(3.14);
show(7, 8);
}
It is not possible to overload two functions by only changing the return type.
If the function name and parameter list are the same, then they are seen as the same by the compiler.
So it gives an error, rather than two overloads, when you give rect two definitions with different return types.
int rect(int value)
{
return value * 2;
}
// double rect(int value)
// {
// return value * 2.0;
// }
Overloading is not based on the return type but on the parameters.
A call is ambiguous when multiple overloads can match the provided arguments and there is no better candidate.
The compiler takes implicit conversions into account when it chooses an overload.
Generally, an exact match is preferable to a conversion. Yet, when two candidates result in equal rank conversions, the compiler is unable to determine which function was meant.
The call then is rejected.
#include <iostream>
void process(int value)
{
std::cout << "process(int)\n";
}
void process(double value)
{
std::cout << "process(double)\n";
}
int main()
{
process(5);
process(5.0);
// process(5.0f);
}
An explicit conversion can eliminate the ambiguity by converting the argument precisely to the type required by one of the overloads.
// Perform this conversion explicitly
process(static_cast<int>(5.0f));
// The f suffix makes this a float literal
process(static_cast<double>(5.0f));
Default arguments can sometimes be a little tricky when dealing with overloaded functions.
A function that takes one argument and another function that takes two arguments can still both handle the same one argument call if the second function supplies a default argument to its final parameter.
The first overload matches because we provide one argument directly. The second overload matches because we can provide the additional argument we were missing with the default.
The compiler is unable to determine which of them to call, resulting in an ambiguous call.
#include <iostream>
void log(int level)
{
std::cout << "Log level " << level << '\n';
}
void log(int level, const char message[] = "none")
{
std::cout << "Log level " << level << " message " << message << '\n';
}
int main()
{
// log(1);
log(1, "test");
}
Take away the default argument or change the name of one of the functions and it becomes an unmistakable choice.
Multiple functions can use the same name by specifying different parameter types or different parameter counts.
One call with one integer will select the integer overload. One call with one floating point will select the floating point overload. One call with two integers will select the two parameter overload.
The return type by itself is not enough to differentiate overloaded functions.
Implicit conversions might lead several overloads to match the same call. Where no other overload is considered a better match it raises an ambiguity error.
Another source of ambiguity is with default arguments, which can enable an entire parameter list to be the same as a shorter overload.
#include <iostream>
void display(int value)
{
std::cout << "Integer: " << value << '\n';
}
void display(double value)
{
std::cout << "Double: " << value << '\n';
}
void display(int first, int second)
{
std::cout << "Two integers: " << first << " and " << second << '\n';
}
int main()
{
display(10);
display(4.5);
display(3, 7);
}
Function overloading requires very little. Each of the parameter lists should be distinct so that the compiler can choose only one function to call, for every call.
Exercise: Function OverloadingStarter code, hints and solutionOpenClose
Practice: Function Overloading
Work through the mission, reveal hints only when needed, and compare your result with the model solution.
Your challenge
Add display(8.0) and predict the chosen overload. Explain why 8.0 and 8 are different argument types.
Mission checklist
- Run the starter as a baseline, then predict the change requested in Your challenge.
- Make that change in your editor; use the lesson-specific hints if you need help.
- Compile and run your answer, then compare with the worked solution and explain the result.
Starter program
This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.
#include <iostream>
void display(int value)
{
std::cout << "Integer: " << value << '\n';
}
void display(double value)
{
std::cout << "Double: " << value << '\n';
}
void display(int first, int second)
{
std::cout << "Two integers: " << first << " and " << second << '\n';
}
int main()
{
display(10);
display(4.5);
display(3, 7);
}Hints
First count the arguments, then inspect their types. display(10), display(4.5), and display(3, 7) match three different parameter lists.
Try display(8.0) rather than display(8). The decimal point makes that literal a double and changes which one-argument overload is the exact match.
Worked solution
display(8.0) selects the double overload because the decimal literal has type double. The selected function returns void and prints Double: 8 using the default stream formatting.
#include <iostream>
void display(int value)
{
std::cout << "Integer: " << value << '\n';
}
void display(double value)
{
std::cout << "Double: " << value << '\n';
}
void display(int first, int second)
{
std::cout << "Two integers: " << first << " and " << second << '\n';
}
int main()
{
display(10);
display(4.5);
display(3, 7);
display(8.0);
}
Example output
Integer: 10
Double: 4.5
Two integers: 3 and 7
Double: 8
0 comments
No comments yet. Be the first.