Functions, Overloading & Defaults
As programs grow, you do not want to write the same lines again and again. A function is a named block of code that does one job. You write it once and call it whenever you need that job done. Functions make code shorter, easier to test and easier to read, because a good name like calculate_fare explains what a block of code does.
In this lesson you will define and call functions, pass values in and get results out, use default arguments, give several functions the same name through overloading, and write a recursive function.
Defining and calling a function
A function definition has four parts: the return type, the name, the parameters in brackets, and the body in braces.
#include <print>
double rectangle_area(double length, double width) {
return length * width;
}
void print_line(int width) {
for (int i = 0; i < width; ++i) {
std::print("=");
}
std::println("");
}
int main() {
print_line(20);
std::println("Area: {} sq m", rectangle_area(4.5, 3.0));
print_line(20);
}==================== Area: 13.5 sq m ====================
rectangle_area takes two double parameters and returns a double. The values you pass when calling, 4.5 and 3.0, are called arguments. The return statement ends the function and sends a value back to the caller. print_line has the return type void, which means it returns nothing; it just does its job.
Declaring before use
The compiler reads your file from top to bottom, so it must know about a function before you call it. If you prefer to put main first, write a declaration (also called a prototype) at the top: the first line of the function followed by a semicolon. The full definition can then come later.
#include <print>
int add_gst(int amount); // declaration
int main() {
std::println("Total: Rs. {}", add_gst(1000));
}
int add_gst(int amount) { // definition
return amount + amount * 18 / 100;
}Total: Rs. 1180
In larger projects, declarations go into header files (.h or .hpp) and definitions into .cpp files, so many files can share the same functions.
Parameters are copies
By default, C++ passes arguments by value: the function receives its own copy. Changing the copy does not change the caller's variable.
#include <print>
void try_to_double(int x) {
x *= 2;
std::println("Inside the function: {}", x);
}
int main() {
int marks = 40;
try_to_double(marks);
std::println("Back in main: {}", marks);
}Inside the function: 80 Back in main: 40
This is safe, but copying large objects such as long strings is slow. In References & Pointers you will learn to pass by reference, which lets a function work on the original without copying.
Default arguments
A default argument is a value the parameter takes when the caller leaves it out. Defaults must be at the end of the parameter list, filling in from the right.
#include <print>
#include <string_view>
void book_ticket(std::string_view name, std::string_view coach = "Sleeper", int berths = 1) {
std::println("{}: {} berth(s) in {}", name, berths, coach);
}
int main() {
book_ticket("Aarav");
book_ticket("Priya", "3A");
book_ticket("Kabir", "2A", 3);
}Aarav: 1 berth(s) in Sleeper Priya: 1 berth(s) in 3A Kabir: 3 berth(s) in 2A
You cannot skip a middle argument: book_ticket("Isha", 2) does not mean "default coach, 2 berths"; it fails to compile because 2 is not text. If a function has a separate declaration, put the default values in the declaration only, not again in the definition.
Function overloading
C++ lets several functions share the same name if their parameter lists differ in number or types. This is overloading. The compiler picks the right version by looking at the arguments, a process called overload resolution.
#include <print>
#include <string>
int total(int a, int b) { return a + b; }
int total(int a, int b, int c) { return a + b + c; }
double total(double a, double b) { return a + b; }
std::string total(const std::string& a, const std::string& b) { return a + " & " + b; }
int main() {
std::println("{}", total(2, 3));
std::println("{}", total(1, 2, 3));
std::println("{}", total(2.5, 1.25));
std::println("{}", total("Meera", "Farhan"));
}5 6 3.75 Meera & Farhan
Two int arguments match the first version exactly, three match the second, two double values match the third, and text converts to std::string for the fourth. Two rules to remember:
- Overloads must differ in their parameters. A different return type alone is not enough.
- A call can be ambiguous.
total(2, 3.5)mixes anintand adouble: theintversion needs one conversion and thedoubleversion needs another, so neither is better. The compiler stops with "call of overloaded 'total(int, double)' is ambiguous".
Recursion and constexpr functions
A function that calls itself is recursive. Every recursive function needs a base case that stops the calls. Factorial is the classic example: 5! = 5 × 4 × 3 × 2 × 1.
#include <print>
constexpr long long factorial(int n) {
if (n <= 1) {
return 1; // base case
}
return n * factorial(n - 1); // recursive case
}
static_assert(factorial(5) == 120); // checked while compiling
int main() {
std::println("10! = {}", factorial(10));
std::println("20! = {}", factorial(20));
}10! = 3628800 20! = 2432902008176640000
Because the function is constexpr, the compiler can run it at compile time. static_assert checks a condition during compilation, so a wrong factorial would stop the build instead of producing a wrong program. Note the long long return type: 20! is far too large for an int, and 21! is too large even for long long.
Common mistakes
- Forgetting
returnin a non-voidfunction. The compiler may only warn, and the caller then receives garbage. Turn on-Wall, which reports "no return statement in function returning non-void". - Calling a function before declaring it. The error is "'name' was not declared in this scope". Add a declaration or move the definition up.
- Recursion without a base case. The function calls itself until the program runs out of stack memory and crashes.
- Expecting a parameter change to reach the caller. Parameters are copies unless you use references.
Key points
- A function has a return type, a name, parameters and a body;
voidmeans no value is returned. - Declare a function before calling it; definitions can come later or live in another file.
- Arguments are passed by value (copied) unless you choose references.
- Default arguments fill in from the right when the caller leaves them out.
- Overloads share a name but must differ in parameters; mixed argument types can make a call ambiguous.
- Recursive functions need a base case;
constexprfunctions can run at compile time.
Exercise
Write two overloads of bmi. The first takes weight in kg and height in metres (both double). The second takes weight in kg and height in centimetres as an int, converts to metres and calls the first, so the formula is written only once. Print the BMI for Rohan (70 kg, 1.75 m) and Isha (52 kg, 160 cm) with one decimal place.
Show solution
#include <print>
double bmi(double weight_kg, double height_m) {
return weight_kg / (height_m * height_m);
}
double bmi(double weight_kg, int height_cm) {
return bmi(weight_kg, height_cm / 100.0);
}
int main() {
std::println("Rohan: {:.1f}", bmi(70.0, 1.75));
std::println("Isha: {:.1f}", bmi(52.0, 160));
}Rohan: 22.9 Isha: 20.3
The second call passes an int height, so the int overload runs and divides by 100.0 to get metres without integer division. It then calls the first overload, because height_cm / 100.0 is a double.
Quick Quiz
1. What does a function with return type void return?
void means the function does its job without sending a value back.
2. Given void f(int a, int b = 5, int c = 10);, which call is valid?
a has no default, so it must be given; f(1, 2) uses the default for c. You cannot skip a middle argument.
3. Two functions are named price. Which pair is a valid overload?
Overloads must differ in their parameter types or count; return types and parameter names do not count.
4. What does every recursive function need?
Without a base case the function keeps calling itself until the stack overflows.
Learn C++ with a mentor
Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
