Skip to content

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.cpp
  • 19.3ReturningFromFunctionsByValue/main.cpp
  • 19.4ReturningByReference/main.cpp
  • 19.5ReturningByPointer/main.cpp
  • 19.6ReturningArrayElementIndexByPointer/main.cpp
  • 19.7BareAutoTypeDeduction/main.cpp
  • 19.8FunctionReturnTypeDeduction/main.cpp
  • 19.9ReturnTypeDeductionWithReferences/main.cpp
  • 19.10FunctionDefinitionsWithReturnTypeDeduction/main.cpp
  • 19.11OptionalOutputFromFunctions/main.cpp
  • 19.12IntroducingStdOptional/main.cpp
  • 19.13OptionalOutputWithStdOptional/main.cpp