Functions, Overloading & Defaults

C++ · Lesson 9 of 32 7 min read Updated

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.

C++
#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);
}
Output
====================
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.

C++
#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;
}
Output
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.

C++
#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);
}
Output
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.

C++
#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);
}
Output
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.

C++
#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"));
}
Output
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 an int and a double: the int version needs one conversion and the double version 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.

C++
#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));
}
Output
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 return in a non-void function. 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; void means 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; constexpr functions 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
C++
#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));
}
Output
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?

2. Given void f(int a, int b = 5, int c = 10);, which call is valid?

3. Two functions are named price. Which pair is a valid overload?

4. What does every recursive function need?

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