Functions

Functions

Write reusable C++ functions — declaration vs definition, parameters, return values, pass by value vs reference, default parameters, inline functions, and lambdas.

Functions

A function is a named, reusable block of code that performs a specific task. Functions are the primary tool for organising C++ programs — they eliminate code duplication, improve readability, and make programs easier to test. Every C++ program is a collection of functions coordinated by main.


Declaring and Defining Functions

cpp
#include <iostream>
using namespace std;

// Declaration (prototype) — tells the compiler the function exists
// Place these at the top or in a header file
double square(double x);
int add(int a, int b);

int main() {
    cout << square(5.0) << "\n";   // 25
    cout << add(3, 4)   << "\n";   // 7
    return 0;
}

// Definitions — can appear after main if declared above
double square(double x) {
    return x * x;
}

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

Without the forward declaration, you would have to define functions before main — declarations give you flexibility in file organisation.


Return Types

cpp
#include <iostream>
using namespace std;

// Returns a value
int multiply(int a, int b) {
    return a * b;
}

// Returns nothing
void printLine(const string& text) {
    cout << text << "\n";
    // No return statement needed (or use bare `return;`)
}

// Early return — exits as soon as condition is met
bool isEven(int n) {
    if (n % 2 == 0) return true;
    return false;
    // Or simply: return (n % 2 == 0);
}

// Multiple return points
string classify(int score) {
    if (score >= 90) return "A";
    if (score >= 80) return "B";
    if (score >= 70) return "C";
    return "F";
}

int main() {
    printLine("Hello from function");
    cout << multiply(4, 7)    << "\n";  // 28
    cout << isEven(10)        << "\n";  // 1
    cout << classify(85)      << "\n";  // B
    return 0;
}

Pass by Value vs Pass by Reference

cpp
#include <iostream>
using namespace std;

// PASS BY VALUE — function gets a COPY; original is unchanged
void doubleByValue(int x) {
    x *= 2;   // Only the local copy is modified
}

// PASS BY REFERENCE — function works on the ORIGINAL variable
void doubleByRef(int& x) {
    x *= 2;   // Modifies the caller's variable
}

// PASS BY CONST REFERENCE — read the original, no copying
double sumVector(const vector<int>& v) {
    double total = 0;
    for (int n : v) total += n;
    return total;
}

int main() {
    int a = 10;
    doubleByValue(a);
    cout << a << "\n";    // 10 — unchanged

    doubleByRef(a);
    cout << a << "\n";    // 20 — modified

    vector<int> nums = {1, 2, 3, 4, 5};
    cout << sumVector(nums) << "\n";  // 15

    return 0;
}
Passing styleSyntaxCopies?Caller's value changed?
By valueint xYesNo
By referenceint& xNoYes
By const referenceconst int& xNoNo
By pointerint* xNo (ptr is copied)Yes (via *x)

Default Parameters

cpp
#include <iostream>
#include <string>
using namespace std;

// Default parameter values — must be in declaration (or definition if no separate declaration)
void greet(const string& name, const string& greeting = "Hello", int times = 1) {
    for (int i = 0; i < times; i++) {
        cout << greeting << ", " << name << "!\n";
    }
}

double power(double base, int exp = 2) {
    double result = 1.0;
    for (int i = 0; i < exp; i++) result *= base;
    return result;
}

int main() {
    greet("Alice");                   // Hello, Alice!
    greet("Bob", "Hi");              // Hi, Bob!
    greet("Charlie", "Hey", 3);     // Hey, Charlie! (×3)

    cout << power(5)     << "\n";   // 25 (5²)
    cout << power(2, 10) << "\n";   // 1024 (2¹⁰)

    return 0;
}

Note: Default parameters must be specified from right to left — you cannot have a non-default parameter after a default one. Once you start using defaults, all subsequent parameters must also have defaults.


Inline Functions

cpp
#include <iostream>
using namespace std;

// The compiler may expand an inline function at the call site
// (eliminates function call overhead for tiny functions)
inline int max(int a, int b) {
    return (a > b) ? a : b;
}

inline double toRadians(double degrees) {
    return degrees * 3.14159265358979 / 180.0;
}

int main() {
    cout << max(10, 20)      << "\n";   // 20
    cout << toRadians(90.0)  << "\n";   // 1.5708
    return 0;
}

inline is a hint to the compiler, not a command — modern compilers inline functions automatically when beneficial.


Returning Multiple Values

C++ functions return one value, but you can return multiple values using:

cpp
#include <iostream>
#include <tuple>
#include <pair>
using namespace std;

// Method 1: Return a pair
pair<double, double> circleStats(double r) {
    const double PI = 3.14159265358979;
    return {PI * r * r, 2 * PI * r};   // {area, circumference}
}

// Method 2: Return a struct
struct DivResult { int quotient; int remainder; };

DivResult divmod(int a, int b) {
    return {a / b, a % b};
}

// Method 3: Return a tuple
tuple<string, int, double> getRecord() {
    return {"Alice", 30, 75000.0};
}

// Method 4: Output parameters (by reference)
void minMax(const vector<int>& v, int& minVal, int& maxVal) {
    minVal = v[0];
    maxVal = v[0];
    for (int n : v) {
        if (n < minVal) minVal = n;
        if (n > maxVal) maxVal = n;
    }
}

int main() {
    auto [area, circ] = circleStats(5.0);   // C++17 structured bindings
    cout << "Area: " << area << ", Circ: " << circ << "\n";

    auto [q, r] = divmod(17, 5);
    cout << "17 ÷ 5 = " << q << " rem " << r << "\n";

    auto [name, age, salary] = getRecord();
    cout << name << " age " << age << " earns " << salary << "\n";

    int lo, hi;
    minMax({3, 1, 4, 1, 5, 9, 2, 6}, lo, hi);
    cout << "Min: " << lo << ", Max: " << hi << "\n";

    return 0;
}
// Output:
// Area: 78.5398, Circ: 31.4159
// 17 ÷ 5 = 3 rem 2
// Alice age 30 earns 75000
// Min: 1, Max: 9

Lambda Functions (C++11)

Lambdas are anonymous (unnamed) functions defined inline:

cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    // Basic lambda: [capture](params) -> return_type { body }
    auto greet = [](const string& name) {
        cout << "Hi, " << name << "!\n";
    };
    greet("Alice");   // Hi, Alice!

    // Lambda with capture (captures local variable by value)
    int threshold = 50;
    auto isAbove = [threshold](int x) { return x > threshold; };
    cout << isAbove(60) << "\n";   // 1 (true)
    cout << isAbove(40) << "\n";   // 0 (false)

    // Capture by reference
    int count = 0;
    auto increment = [&count]() { count++; };
    increment(); increment(); increment();
    cout << count << "\n";   // 3

    // Lambda with STL algorithms
    vector<int> nums = {5, 2, 8, 1, 9, 3, 7, 4, 6};

    sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; });  // Descending
    for (int n : nums) cout << n << " ";
    cout << "\n";
    // 9 8 7 6 5 4 3 2 1

    int sum = 0;
    for_each(nums.begin(), nums.end(), [&sum](int n) { sum += n; });
    cout << "Sum: " << sum << "\n";  // 45

    return 0;
}

Practical Example: Statistics Calculator

cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <numeric>
using namespace std;

double mean(const vector<double>& v) {
    return accumulate(v.begin(), v.end(), 0.0) / v.size();
}

double median(vector<double> v) {   // Copy — sorting modifies the vector
    sort(v.begin(), v.end());
    int n = v.size();
    return (n % 2 == 0)
        ? (v[n/2 - 1] + v[n/2]) / 2.0
        : v[n/2];
}

double stddev(const vector<double>& v) {
    double m = mean(v);
    double variance = 0.0;
    for (double x : v) {
        variance += (x - m) * (x - m);
    }
    return sqrt(variance / v.size());
}

pair<double, double> range(const vector<double>& v) {
    auto [minIt, maxIt] = minmax_element(v.begin(), v.end());
    return {*minIt, *maxIt};
}

int main() {
    vector<double> data = {4, 8, 6, 5, 3, 2, 8, 9, 2, 5};

    auto [lo, hi] = range(data);

    cout << "Data: ";
    for (double x : data) cout << x << " ";
    cout << "\n\n";

    cout << "Count:   " << data.size() << "\n";
    cout << "Mean:    " << mean(data)  << "\n";
    cout << "Median:  " << median(data) << "\n";
    cout << "Std Dev: " << stddev(data) << "\n";
    cout << "Min:     " << lo << "\n";
    cout << "Max:     " << hi << "\n";
    // Count:   10
    // Mean:    5.2
    // Median:  5
    // Std Dev: 2.27...
    // Min:     2
    // Max:     9

    return 0;
}

Summary

  • Functions have a return type, name, parameters, and body; forward declarations let you define them after main
  • Pass by value copies the argument — the original is safe; pass by reference lets the function modify the original
  • Always use const& for large objects (strings, vectors) you only need to read — avoids copying without allowing modification
  • Default parameters (right to left) let callers omit trailing arguments
  • Return multiple values with pair, tuple, a struct, or output parameters
  • Lambdas are anonymous functions; use [=] to capture by value, [&] to capture by reference
  • inline hints the compiler to expand small functions at the call site — modern compilers decide this automatically

Next, we'll explore function overloading — defining multiple functions with the same name but different parameter lists.