Site

Functions — Definitions and Overloading

Tutorial 4.0  •  C++ / Learn

4.0 What This Teaches

Functions are the primary unit of reuse in C++. This tutorial covers:

4.1 Defining a Function

int add(int a, int b) {
    return a + b;
}
The return type comes first, then the name, then the parameter list. Functions must be defined or declared before they are called, unless they are defined after main with a forward declaration at the top:
int add(int a, int b);   // forward declaration

int main() {
    std::cout << add(3, 4) << "\n";
}

int add(int a, int b) { return a + b; }

4.2 Default Arguments

double circle_area(double radius, double pi = 3.14159265) {
    return pi * radius * radius;
}

circle_area(5.0);              // uses default pi
circle_area(5.0, 3.14159265);  // explicit pi
Default arguments must appear at the end of the parameter list. If a parameter has a default, all parameters to its right must also have defaults.

4.3 Function Overloading

void print_value(int x)         { std::cout << "int: "    << x << "\n"; }
void print_value(double x)      { std::cout << "double: " << x << "\n"; }
void print_value(std::string s) { std::cout << "string: " << s << "\n"; }
The compiler selects the correct overload based on the argument types. Two functions may have the same name as long as their parameter lists differ in type, number, or order. Return type alone is not sufficient to distinguish overloads.

4.4 Pass by Value, Reference, and Const Reference

// pass by value: caller's copy is unchanged
void double_it(int x) { x *= 2; }

// pass by reference: caller's variable is modified
void double_in_place(int& x) { x *= 2; }

// pass by const reference: no copy, no modification (best for large types)
void print_string(const std::string& s) { std::cout << s << "\n"; }
Pass large objects (std::string, std::vector, structs) by const& to avoid copying. Pass small types (int, double, pointers) by value.

4.5 Returning Values

std::string make_greeting(const std::string& name) {
    return "Hello, " + name + "!";
}

// return type void: no return value
void greet(const std::string& name) {
    std::cout << make_greeting(name) << "\n";
}
Return by value copies (or moves) the result to the caller. Modern compilers apply return-value optimization (RVO) and avoid the copy in most cases.

4.6 Example - All Together

// Functions - definitions, overloading, default args, and pass by reference.

#include <iostream>
#include <string>
#include <cmath>

int add(int a, int b) { return a + b; }

double circle_area(double radius, double pi = 3.14159265) {
    return pi * radius * radius;
}

void print_value(int x)         { std::cout << "int: "    << x << "\n"; }
void print_value(double x)      { std::cout << "double: " << x << "\n"; }
void print_value(std::string s) { std::cout << "string: " << s << "\n"; }

void swap_values(int& a, int& b) {
    int tmp = a; a = b; b = tmp;
}

int main() {
    std::cout << "add(3,4) = " << add(3, 4) << "\n";

    std::cout << "circle_area(5) = " << circle_area(5.0) << "\n";

    print_value(42);
    print_value(3.14);
    print_value(std::string("hello"));

    int x = 10, y = 20;
    swap_values(x, y);
    std::cout << "after swap: x=" << x << " y=" << y << "\n";

    return 0;
}
add(3,4) = 7
circle_area(5) = 78.5398
int: 42
double: 3.14
string: hello
after swap: x=20 y=10

4.7 Exercise

Exercise
  • Write an overloaded max_of function that works for int, double, and std::string.
  • Write a function clamp(int value, int lo, int hi) that returns value clamped to the inclusive range [lo, hi].
  • Write a function that takes a std::string& by reference and converts it to uppercase in place using std::toupper on each character.

4.8 Common Mistakes

Expecting pass-by-value to modify the caller's variable

void increment(int x) { x++; }   // modifies a copy, not the caller's variable
int n = 5;
increment(n);
std::cout << n;   // still 5
Use int& x to modify the caller's variable.

Returning a reference to a local variable

int& bad() {
    int local = 42;
    return local;   // dangling reference: local is destroyed on return
}
Only return references to variables that outlive the function (members, parameters, statics).

Ambiguous overloads

void f(int x) {}
void f(double x) {}
f(3.0f);   // error: ambiguous - float converts equally well to int or double
Add an overload for float or cast the argument explicitly.

4.9 Key Terms

TermMeaning
forward declarationDeclares a function's signature without its body; allows calling it before its definition
default argumentA parameter value used when the caller does not supply one
overloadingMultiple functions with the same name but different parameter types
pass by valueCaller's argument is copied; changes do not affect the caller
pass by reference (&)Function operates on the caller's variable directly
pass by const referenceNo copy, no modification; best for large types
RVOReturn Value Optimization: compiler elides the copy when returning a local object