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.
constexprfunctions suggest compile-time evaluation, whereasconstevalfunctions 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.cpp16.Functions/16.3FunctionDeclarationsAndDefinitions/main.cpp16.Functions/16.4MultipleFiles_CompilationModelRevisited/main.cpp16.Functions/16.5PassByValue/main.cpp16.Functions/16.6PassByConstValue/main.cpp16.Functions/16.7PassByPointer/main.cpp16.Functions/16.8PassByPointerToConst/main.cpp16.Functions/16.9PassByConstPointerToConst/main.cpp16.Functions/16.10PassByReference/main.cpp16.Functions/16.11PassByConstReference/main.cpp16.Functions/16.13ArrayFunctionParameters/main.cpp16.Functions/16.15SizedArraysByReference/main.cpp16.Functions/16.16MultiDimensionalArrayFunctionParameters/main.cpp16.Functions/16.17DefaultFunctionParameters/main.cpp16.Functions/16.18ImplicitConversions/main.cpp16.Functions/16.19ImplicitConversionsWithReferences/main.cpp16.Functions/16.20ImplicitConversionsWithPointers/main.cpp16.Functions/16.21String_viewParameters/main.cpp16.Functions/16.22ImplicitConversionsFromStringViewToString/main.cpp16.Functions/16.23constexprFunctions/main.cpp16.Functions/16.24constevalFunctions/main.cpp17.EnumsAndTypeAliases/17.2EnumClasses/main.cpp17.EnumsAndTypeAliases/17.3UsingEnum/main.cpp17.EnumsAndTypeAliases/17.4OldEnums/main.cpp17.EnumsAndTypeAliases/17.5TypeAliases/main.cpp18.ArgumentsToTheMainFunction/18.2GrabAndUseTheArguments/main.cpp18.ArgumentsToTheMainFunction/18.3CalculatorV1/main.cpp18.ArgumentsToTheMainFunction/18.4CalculatorV2/main.cpp