Skip to content

Ch11: Functions

TL;DR: One Definition Rule (ODR): Freestanding variables and functions can only be defined once across the program, whereas classes can be defined in multiple translation units.

โšก Quick Reference

#include <iostream>

// Function declaration (prototype)
void process(int val);

// Pass by value: copies values, original variable is safe.
void pass_value(int num) { num += 10; }

// Pass by reference: direct alias, mutations affect original.
void pass_ref(int& num) { num += 10; }

// Pass by pointer: accepts memory address, checks for null.
void pass_ptr(int* p_num) {
    if (p_num) {
        *p_num += 10;
    }
}

int main() {
    int x = 10;
    pass_value(x);
    pass_ref(x);
    pass_ptr(&x);
    return 0;
}

๐Ÿง  Core Concepts

  • One Definition Rule (ODR): Freestanding variables and functions can only be defined once across the program, whereas classes can be defined in multiple translation units.
  • Function Parameter Passing: Arguments are passed by value (copied) by default. Pass-by-reference (&) or pass-by-pointer (*) allows direct modification of the caller's variables.
  • Compilation & Compilation Constraints: Preprocessor imports headers. constexpr functions suggest compile-time evaluation, whereas consteval functions guarantee compile-time execution.

โš ๏ธ Pitfalls (Quick Scan)

Mistake Fix
Defining a freestanding variable or function twice across files Ensure each non-class symbol has exactly one definition
Defining the same class multiple times within one translation unit Use include guards or #pragma once to prevent double inclusion
Defining out-of-line class members in multiple translation units Implement static members and out-of-line methods in a single source file
Declaring two functions with identical name and parameter types Ensure every function has a unique signature of name and parameters
Declaring a function without providing an implementation Provide definitions in compiled source files for all declared functions
Expecting caller's variables to modify when using pass-by-value Use pointer (*) or reference (&) parameters when mutation is required
Allowing implicit double-to-int conversions in arguments Match argument types explicitly to parameter types to avoid narrowing
Passing a convertible type implicitly to a non-const reference parameter Avoid conversions for reference parameters, or use const T& for read-only access
Attempting to implicitly convert std::string_view to std::string Use explicit construction: std::string(name_sv)
Using postfix a++ instead of prefix ++a when immediate value is needed Use prefix ++a to increment and retrieve the new value immediately
Using raw asterisk (``) on the command line for multiplication Escape the asterisk or use a placeholder character like x in CLI calculators
Assuming sizeof returns static array size inside functions Pass array size explicitly or pass the array by reference: T (&arr)[N]
Declaring constinit variables with non-constant expressions Only initialize constinit with constexpr values or constant literals

๐Ÿ“– Full Details

Cause โ†’ Effect โ†’ Fix with timestamp (click to expand) * **Defining a freestanding variable or function twice across files** -> **Linker error "multiple definition of" preventing binary creation** -> **Ensure each non-class symbol has exactly one definition (01:17)** * **Defining the same class multiple times within one translation unit** -> **Compile-time error "redefinition of class"** -> **Use include guards or `#pragma once` to prevent double inclusion (03:42)** * **Defining out-of-line class members in multiple translation units** -> **Linker error "multiple definition" of class constructors/methods** -> **Implement static members and out-of-line methods in a single source file (06:11)** * **Declaring two functions with identical name and parameter types** -> **Compile-time error "redefinition" even if return types or parameter names differ** -> **Ensure every function has a unique signature of name and parameters (18:25)** * **Declaring a function without providing an implementation** -> **Linker error "undefined reference" or "unresolved external symbol"** -> **Provide definitions in compiled source files for all declared functions (33:52)** * **Expecting caller's variables to modify when using pass-by-value** -> **Modifications only affect local function copies, leaving original variable unchanged** -> **Use pointer (`*`) or reference (`&`) parameters when mutation is required (40:15)** * **Allowing implicit double-to-int conversions in arguments** -> **Decimal values are silently truncated, leading to calculation inaccuracies** -> **Match argument types explicitly to parameter types to avoid narrowing (10:33)** * **Passing a convertible type implicitly to a non-const reference parameter** -> **Compiler binds reference to a temporary copy, modifying the copy instead of the variable** -> **Avoid conversions for reference parameters, or use `const T&` for read-only access (41:00)** * **Attempting to implicitly convert `std::string_view` to `std::string`** -> **Compile-time error because implicit conversion is disabled for safety** -> **Use explicit construction: `std::string(name_sv)` (40:15)** * **Using postfix `a++` instead of prefix `++a` when immediate value is needed** -> **Returns the unincremented value first, leading to arithmetic mistakes** -> **Use prefix `++a` to increment and retrieve the new value immediately (37:30)** * **Using raw asterisk (`*`) on the command line for multiplication** -> **Shell globbing expands `*` into local filenames, corrupting calculator arguments** -> **Escape the asterisk or use a placeholder character like `x` in CLI calculators (49:30)** * **Assuming `sizeof` returns static array size inside functions** -> **Array decays to a raw pointer, and `sizeof` returns pointer size** -> **Pass array size explicitly or pass the array by reference: `T (&arr)[N]` (39:15)** * **Declaring `constinit` variables with non-constant expressions** -> **Compile-time error because initializers are not evaluatable during build** -> **Only initialize `constinit` with `constexpr` values or constant literals (53:00)**

๐Ÿ“Ž Repo Files

  • 16.Functions/16.2FirstHandOnCppFunctions/main.cpp
  • 16.Functions/16.3FunctionDeclarationsAndDefinitions/main.cpp
  • 16.Functions/16.4MultipleFiles_CompilationModelRevisited/main.cpp
  • 16.Functions/16.5PassByValue/main.cpp
  • 16.Functions/16.6PassByConstValue/main.cpp
  • 16.Functions/16.7PassByPointer/main.cpp
  • 16.Functions/16.8PassByPointerToConst/main.cpp
  • 16.Functions/16.9PassByConstPointerToConst/main.cpp
  • 16.Functions/16.10PassByReference/main.cpp
  • 16.Functions/16.11PassByConstReference/main.cpp
  • 16.Functions/16.13ArrayFunctionParameters/main.cpp
  • 16.Functions/16.15SizedArraysByReference/main.cpp
  • 16.Functions/16.16MultiDimensionalArrayFunctionParameters/main.cpp
  • 16.Functions/16.17DefaultFunctionParameters/main.cpp
  • 16.Functions/16.18ImplicitConversions/main.cpp
  • 16.Functions/16.19ImplicitConversionsWithReferences/main.cpp
  • 16.Functions/16.20ImplicitConversionsWithPointers/main.cpp
  • 16.Functions/16.21String_viewParameters/main.cpp
  • 16.Functions/16.22ImplicitConversionsFromStringViewToString/main.cpp
  • 16.Functions/16.23constexprFunctions/main.cpp
  • 16.Functions/16.24constevalFunctions/main.cpp
  • 17.EnumsAndTypeAliases/17.2EnumClasses/main.cpp
  • 17.EnumsAndTypeAliases/17.3UsingEnum/main.cpp
  • 17.EnumsAndTypeAliases/17.4OldEnums/main.cpp
  • 17.EnumsAndTypeAliases/17.5TypeAliases/main.cpp
  • 18.ArgumentsToTheMainFunction/18.2GrabAndUseTheArguments/main.cpp
  • 18.ArgumentsToTheMainFunction/18.3CalculatorV1/main.cpp
  • 18.ArgumentsToTheMainFunction/18.4CalculatorV2/main.cpp