Overloading is everywhere in the standard library and is the basis for operator overloading and templates.
C++ lets several functions share a name as long as their parameter lists differ. At each call the compiler picks the best match by overload resolution: an exact match beats a promotion (like char to int or float to double), which beats a standard conversion (like int to double), which beats a user-defined conversion (like const char* to std::string).
Overloads differ by parameters only. Two functions that differ only in return type can't coexist, because the call f(1) wouldn't tell them apart.
If two candidates are equally good, the call is ambiguous and won't compile. A classic example is calling print(3L) when only print(int) and print(double) exist: converting long to either is a standard conversion, so neither wins. The fix is an explicit cast or another overload.
Overloading on T versus const T& for the same type is usually ambiguous too, so pick one parameter style per type.
Default arguments make trailing parameters optional: pad(std::string s, std::size_t width = 8, char fill = '.') can be called with one, two or three arguments. Defaults must come at the end of the list, and they belong in the declaration that callers see (usually the header), not repeated in the definition.
Use overloading when the functions do the same thing for different types (like std::to_string). If the behaviour differs, give them different names. Prefer a default argument over a second overload when only one value changes. Templates, covered later, write the overloads for you when the code is identical for every type.
Example
#include <iostream>
#include <string>
void print(int n) { std::cout << "int " << n << '\n'; }
void print(double d) { std::cout << "double " << d << '\n'; }
void print(const std::string& s) { std::cout << "string " << s << '\n'; }
std::string pad(std::string s, std::size_t width = 8, char fill = '.') {
if (s.size() < width) s.append(width - s.size(), fill);
return s;
}
int main() {
print(3); // int 3
print(2.5); // double 2.5
print("hey"); // string hey (user-defined conversion from const char*)
print('A'); // int 65: char -> int is a promotion
// print(3L); // error: ambiguous, long -> int and long -> double tie
std::cout << pad("ab") << '|' << pad("ab", 4) << '|' << pad("ab", 4, '-') << '\n';
// ab......|ab..|ab--
}Remember
- Overloads must differ in parameters, never only in return type
- Exact match > promotion > conversion > user-defined conversion; ties are errors
- Default arguments go at the end, in the declaration
Watch out for
- Repeating the default value in both the header declaration and the .cpp definition — that's a compile error.