# Functions and Parameter Passing — C++

Source: https://www.skillbyai.com/en/cpp/c-functions

> Pass arguments by value, reference and const reference, and use overloading and constexpr.

## Choosing how to pass arguments

How you pass arguments affects both performance and correctness. **By value** (`void f(int x)`) copies the argument: ideal for small, cheap types such as `int`, `double` and small structs, and for parameters the function will keep a copy of anyway. **By const reference** (`void f(const std::string& s)`) avoids copying large objects while promising not to modify them: the default for read-only inputs of class type. **By non-const reference** (`void f(std::vector<int>& v)`) lets the function modify the caller's object: use it sparingly and make it obvious, because returning a value is usually clearer. **By pointer** is for optional arguments (`nullptr` meaning "none") or C interfaces. Return results **by value**: compilers apply copy elision and move semantics, so returning a `std::vector` is cheap. Functions can be **overloaded** by parameter types, have **default arguments**, and be marked **`constexpr`** so they can run at compile time when given constant inputs. Use **`[[nodiscard]]`** on functions whose return value must not be ignored.

## Parameter passing in practice

Small types by value, large inputs by const reference, results returned by value.

```cpp
#include <cctype>
#include <numeric>
#include <string>
#include <vector>

constexpr int square(int x) { return x * x; }          // can run at compile time
static_assert(square(12) == 144);

[[nodiscard]] double average(const std::vector<int>& values) {   // read-only, no copy
    if (values.empty()) return 0.0;
    return std::accumulate(values.begin(), values.end(), 0.0) / values.size();
}

void normalise(std::string& name) {                     // modifies the caller's string
    for (auto& ch : name) ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}

std::vector<int> evens_up_to(int n) {                   // return by value: cheap (move/elision)
    std::vector<int> out;
    for (int i = 0; i <= n; i += 2) out.push_back(i);
    return out;
}

std::string greet(const std::string& name, const std::string& greeting = "Hello") {
    return greeting + ", " + name;
}
```

## Lending a book

Passing by value is handing someone a photocopy. Passing by const reference is lending the original with a "read only" sticker. Passing by non-const reference is lending it with permission to write in the margins, so make sure that is what you intend.

**Quiz:** What is the usual way to pass a large std::vector that a function only reads?

- [ ] By value
- [x] By const reference
- [ ] By non-const reference
- [ ] As a global variable

*Answer:* By const reference. const reference avoids copying while guaranteeing the function will not modify the argument.
