C++: Function Overloading and Default Arguments
Last updated: 2026-08-26
Sometimes you need a function that can handle different types or different numbers of parameters.
For example, a
C++ provides two features for this — function overloading and default arguments.
1. Function Overloading
(1) 1.1 What is Function Overloading?
Function overloading allows you to define multiple functions with the same name, but with different parameter lists.
| Function Signature | Meaning |
|---|---|
print(int x) |
Print an integer |
print(double x) |
Print a floating-point number |
print(const std::string& x) |
Print a string |
When called, the compiler automatically selects the correct function based on the argument types.
▶ Example 1: Function Overloading (Difficulty ⭐)
#include <iostream>
#include <string>
void print(int x) {
std::cout << "Integer: " << x << std::endl;
}
void print(double x) {
std::cout << "Float: " << x << std::endl;
}
void print(const std::string& x) {
std::cout << "String: " << x << std::endl;
}
int main() {
print(5); // Calls print(int)
print(3.14); // Calls print(double)
print("Hello"); // Calls print(const char*)
return 0;
}
Output:
Integer: 5
Float: 3.14
String: Hello
⚠️ Key point: The condition for function overloading is that the parameter list must differ (different types, different count, or different order). A different return type alone does not constitute overloading.
2. Rules for Function Overloading
(1) 2.1 What Counts as "Different Parameter List"?
| Difference | Example |
|---|---|
| Different types | void foo(int x) vs void foo(double x) |
| Different count | void foo(int x) vs void foo(int x, int y) |
| Different order | void foo(int x, double y) vs void foo(double x, int y) |
(2) 2.2 What Does NOT Count as Overloading?
| Situation | Example | Result |
|---|---|---|
| Only return type differs | int foo() vs double foo() |
❌ Compile error: redefinition |
| Only parameter names differ | void foo(int x) vs void foo(int y) |
❌ Compile error: redefinition |
3. Default Arguments
(1) 3.1 Basic Usage
Default arguments allow you to omit certain arguments when calling a function — the compiler will automatically use the default values.
#include <iostream>
void print(int x, int y = 10) {
std::cout << "x = " << x << ", y = " << y << std::endl;
}
int main() {
print(5); // Only pass one argument, y uses default value 10
print(5, 20); // Pass two arguments, y is changed to 20
return 0;
}
(2) 3.2 Rules for Default Arguments
- Default values can only be set from right to left
void foo(int a, int b = 10, int c = 20); // ✅ Correct
void bar(int a = 5, int b, int c); // ❌ Error: a has default but b doesn't
- Default arguments can only be specified once — in the prototype or the definition
int add(int a, int b = 10); // Specify default in the prototype
int main() {
std::cout << add(5) << std::endl;
return 0;
}
int add(int a, int b) { // ✅ Don't write the default again in the definition
return a + b;
}
4. Function Overloading + Default Arguments
Function overloading and default arguments can be used together to make functions more flexible.
▶ Example 2: Print Array with Delimiter (Difficulty ⭐⭐)
#include <iostream>
#include <string>
void printArray(int arr, int length) {
printArray(arr, length, ", ");
}
void printArray(int arr, int length, const std::string& delimiter) {
for (int i = 0; i < length; i++) {
std::cout << arr[i];
if (i < length - 1) {
std::cout << delimiter;
}
}
std::cout << std::endl;
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printArray(arr, 5); // Use default delimiter ", "
printArray(arr, 5, " - "); // Use custom delimiter " - "
return 0;
}
Output:
1, 2, 3, 4, 5
1 - 2 - 3 - 4 - 5
5. Practice: Simple Logging Function
▶ Example 3: Print Function with Log Levels (Difficulty ⭐⭐)
#include <iostream>
#include <string>
#include <ctime>
enum LogLevel {
INFO,
WARNING,
ERROR
};
std::string getCurrentTime() {
std::time_t now = std::time(nullptr);
std::tm* tm = std::localtime(&now);
char buffer[80];
std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm);
return std::string(buffer);
}
void log(const std::string& message) {
log(message, INFO);
}
void log(const std::string& message, LogLevel level) {
std::string levelStr;
if (level == INFO) {
levelStr = "INFO";
} else if (level == WARNING) {
levelStr = "WARNING";
} else {
levelStr = "ERROR";
}
std::cout << "[" << getCurrentTime() << "] [" << levelStr << "] " << message << std::endl;
}
int main() {
log("Program started");
log("Insufficient memory", WARNING);
log("File open failed", ERROR);
return 0;
}
Output:
[] []
Output (example):
[2026-06-28 22:30:00] [INFO] Program started
[2026-06-28 22:30:01] [WARNING] Insufficient memory
[2026-06-28 22:30:02] [ERROR] File open failed
6. Common Errors
(1) 6.1 Function Overloading Ambiguity
Error example:
#include <iostream>
void foo(int x) {
std::cout << "foo(int)" << std::endl;
}
void foo(double x) {
std::cout << "foo(double)" << std::endl;
}
int main() {
foo(5); // ✅ Calls foo(int)
foo(3.14); // ✅ Calls foo(double)
foo('A'); // ❌ Ambiguous: char can convert to int or double
return 0;
}
Compile error message:
error: call to 'foo' is ambiguous
Fix: Explicit type conversion:
foo(static_cast<int>('A')); // Calls foo(int)
foo(static_cast<double>('A')); // Calls foo(double)
(2) 6.2 Default Arguments Causing Overload Ambiguity
Error example:
#include <iostream>
void foo(int x) {
std::cout << "foo(int)" << std::endl;
}
void foo(int x, int y = 10) {
std::cout << "foo(int, int)" << std::endl;
}
int main() {
foo(5); // ❌ Ambiguous: can call foo(int) or foo(int, int) with default
return 0;
}
Fix: Remove one of the overloads, or change a parameter type.
❓ FAQ
Q: What's the difference between function overloading and C-style functions? A: C doesn't support function overloading because the C compiler's name mangling rules are simple (basically just the function name itself). C++ compilers perform name mangling, encoding parameter type information into the function name, so same-named functions can coexist.
Q: Can default arguments be variables? A: Yes, but they must be global or static variables (covered later).
CPPint defaultY = 10; void foo(int x, int y = defaultY) { // Can be a variable // ... }
Q: Does function overloading improve runtime efficiency? A: No. Function overloading is a compile-time feature (the compiler selects the function based on argument types); runtime efficiency is the same as a regular function call. It improves development efficiency (code is more concise and readable).
📖 Summary
- Function overloading allows defining multiple same-named functions with different parameter lists
- Default arguments allow omitting certain arguments; the compiler fills in default values
- Default arguments can only be set from right to left
- Function overloading + default arguments make functions more flexible
- Be careful to avoid overload ambiguity
📝 Exercises
-
Basic (Difficulty ⭐): Write overloaded functions
int max(int a, int b)anddouble max(double a, double b)that return the maximum of two integers and two floating-point numbers respectively. -
Intermediate (Difficulty ⭐⭐): Write a function
void drawRectangle(int width, int height, char fillChar = '*')that prints a rectangle.
Input: drawRectangle(5, 3, '#')
Output:
#####
#####
#####
(When fillChar is not passed, use the default *)
- Challenge (Difficulty ⭐⭐⭐): Write a program implementing a "multi-function calculator":
- Use function overloading to implement
add,subtract,multiply,dividefor bothintanddoubletypes - Use default arguments to set "decimal places" (default 2)
- Test all functions in
main
- Function overloading: same name, different parameters (different count or types)
- Overload resolution: compiler matches the best version based on argument types
- Be careful about ambiguity when mixing default arguments and overloading
- Cannot overload by return type alone
- Overloading makes code more intuitive, using one function name for different types
11. 🚀 Next Step
Now that you've learned advanced function usage, next we'll learn scope and lifetime (Lesson 14) — understanding where variables can be accessed and how long they exist, which is key to understanding program behavior!