Ch12: Getting Things out of Functions¶
TL;DR: Output Parameters: Functions can output results by writing directly into variables passed by non-const reference (
T&) or pointer (T*).
โก Quick Reference¶
#include <iostream>
#include <string>
// Input, Output, and In-Out parameter patterns
void process(const int& input, int* output, int& in_out) {
if (output) {
*output = input * 2; // Output parameter writes result
}
in_out += input; // In-Out parameter reads and modifies
}
// Returning by value: triggers copy or return value optimization (RVO)
int get_value() { return 42; }
// Returning by pointer: returns memory address of heap or static resources
int* get_ptr() {
static int val = 10;
return &val;
}
// Returning by reference: returns reference to permanent state
int& get_ref() {
static int val = 20;
return val;
}
// auto - compiler deduces return type based on return expression
auto deduce() { return 3.14; }
int main() {
int in = 5, out = 0, io = 10;
process(in, &out, io);
return 0;
}
๐ง Core Concepts¶
- Output Parameters: Functions can output results by writing directly into variables passed by non-const reference (
T&) or pointer (T*). - Returning by Reference or Pointer: Functions can return references or pointers to existing objects, but returning pointers or references to local variables causes undefined behavior.
- Optional Results (
std::optional):std::optional<T>represents a value that may or may not exist, offering a type-safe alternative to sentinel error values.
โ ๏ธ Pitfalls (Quick Scan)¶
| Mistake | Fix |
|---|---|
Marking reference output parameters as const |
Keep output parameters as non-const references (T&) or pointers (T*) |
| Relying on return-by-value to copy objects for safety | Never write code that relies on return copies being created |
Returning a reference (T&) to a local variable |
Only return references to variables that outlive the function scope |
Returning a pointer (T) pointing to a local variable |
Only return pointers to variables that outlive the function scope |
Using bare auto to deduce references |
Explicitly declare variable as auto& to capture reference semantics |
Relying on auto return deduction to return a reference |
Specify T& explicitly or use decltype(auto) for reference return deduction |
Calling .value() on an empty std::optional |
Verify existence with has_value() or retrieve safely using value_or() |
Returning different types from different branches in an auto-deduced function |
Cast return expressions explicitly to a common type using static_cast |
Forgetting the address-of operator & when passing variable to a pointer parameter |
Always prefix variables with & when passing to pointer output parameters |
Using arbitrary sentinel values (e.g., -1) to represent errors/absence |
Use std::optional to explicitly represent the absence of a value |
๐ Full Details¶
Cause โ Effect โ Fix with timestamp (click to expand)
* **Marking reference output parameters as `const`** -> **Compile-time error when attempting to modify the parameter inside the function body** -> **Keep output parameters as non-const references (`T&`) or pointers (`T*`) (00:40)** * **Relying on return-by-value to copy objects for safety** -> **Compiler optimizes away copies (copy elision), giving variables identical memory addresses** -> **Never write code that relies on return copies being created (07:30)** * **Returning a reference (`T&`) to a local variable** -> **Undefined behavior because reference dangles when local variable is destroyed at exit** -> **Only return references to variables that outlive the function scope (09:15)** * **Returning a pointer (`T*`) pointing to a local variable** -> **Undefined behavior because pointer dangles after local variable is destroyed** -> **Only return pointers to variables that outlive the function scope (10:45)** * **Using bare `auto` to deduce references** -> **Deduces a separate copy instead of a reference alias, losing modification tracking** -> **Explicitly declare variable as `auto&` to capture reference semantics (13:15)** * **Relying on `auto` return deduction to return a reference** -> **Compiler deduces the return type by value, copying data instead of returning reference** -> **Specify `T&` explicitly or use `decltype(auto)` for reference return deduction (16:10)** * **Calling `.value()` on an empty `std::optional`** -> **Throws `std::bad_optional_access` exception and crashes the program** -> **Verify existence with `has_value()` or retrieve safely using `value_or()` (19:10)** * **Returning different types from different branches in an `auto`-deduced function** -> **Compile-time error because compiler cannot deduce a single return type** -> **Cast return expressions explicitly to a common type using `static_cast` (15:15)** * **Forgetting the address-of operator `&` when passing variable to a pointer parameter** -> **Compile-time error because function expects `T*` but receives `T`** -> **Always prefix variables with `&` when passing to pointer output parameters (01:50)** * **Using arbitrary sentinel values (e.g., `-1`) to represent errors/absence** -> **Ambiguity for callers and risk of compiler conversion errors** -> **Use `std::optional` to explicitly represent the absence of a value (17:00)**๐ Repo Files¶
19.2InputAndOutputParameters/main.cpp19.3ReturningFromFunctionsByValue/main.cpp19.4ReturningByReference/main.cpp19.5ReturningByPointer/main.cpp19.6ReturningArrayElementIndexByPointer/main.cpp19.7BareAutoTypeDeduction/main.cpp19.8FunctionReturnTypeDeduction/main.cpp19.9ReturnTypeDeductionWithReferences/main.cpp19.10FunctionDefinitionsWithReturnTypeDeduction/main.cpp19.11OptionalOutputFromFunctions/main.cpp19.12IntroducingStdOptional/main.cpp19.13OptionalOutputWithStdOptional/main.cpp