Skip to main content

Updated Jun 4, 2026

Inline Functions

inline suggests the compiler replace function calls with function body, eliminating call overhead. Modern compilers decide automatically.

Suggestion, Not Command

inline is a hint. Compilers inline automatically based on optimization settings, often ignoring the keyword.

Basic Usage​

// Without inline
int square(int x) {
return x * x;
}

int result = square(5); // Function call overhead

// With inline
inline int square(int x) {
return x * x;
}

int result = square(5); // May be replaced with: 5 * 5

How Inlining Works​

Normal Function Call​

int add(int a, int b) {
return a + b;
}

int x = add(5, 3);

// Assembly (simplified):
// push 3
// push 5
// call add
// add esp, 8
// mov x, eax

Inlined Function​

inline int add(int a, int b) {
return a + b;
}

int x = add(5, 3);

// Assembly (simplified):
// mov eax, 5
// add eax, 3
// mov x, eax

No call overhead: no stack manipulation, no jump, faster execution.


When to Inline​

Good Candidates​

// Small, frequently called functions
inline int max(int a, int b) {
return a > b ? a : b;
}

// Getters/setters
class Widget {
int value;
public:
inline int getValue() const { return value; }
inline void setValue(int v) { value = v; }
};

// Simple calculations
inline double square(double x) { return x * x; }

Poor Candidates​

// Large functions (defeats purpose)
inline void processComplexData(/*...*/) {
// 100 lines of code
// Inlining duplicates code everywhere
}

// Recursive functions (compiler won't inline)
inline int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}

// Virtual functions (can't inline through base pointer)
class Base {
public:
virtual inline void func() {} // inline ineffective
};

Definition Placement​

Inline functions must be defined in headers (visible to all translation units):

// widget.h
#pragma once

class Widget {
public:
// Implicitly inline (defined in class)
int getValue() const { return value; }

// Explicitly inline
inline void setValue(int v);

private:
int value;
};

// Still in header
inline void Widget::setValue(int v) {
value = v;
}
One Definition Rule

Inline functions can be defined in multiple translation units (identical definitions required).


Implicit Inline​

Member Functions Defined in Class​

class Widget {
public:
// Implicitly inline
int getValue() const {
return value;
}

// Also implicitly inline
void setValue(int v) { value = v; }

private:
int value;
};

constexpr Functions​

// constexpr is implicitly inline
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}

Modern Compiler Behavior​

Automatic Inlining​

// Even without 'inline', compilers inline with optimization
int square(int x) {
return x * x;
}

// With -O2 or -O3, this is inlined automatically

Force Inline (Compiler-Specific)​

// GCC/Clang
__attribute__((always_inline))
inline int add(int a, int b) {
return a + b;
}

// MSVC
__forceinline int add(int a, int b) {
return a + b;
}

Prevent Inline​

// GCC/Clang
__attribute__((noinline))
void debug_func() {
// Always appears in stack traces
}

// MSVC
__declspec(noinline) void debug_func() {
// ...
}

Performance Impact​

Benefits​

// Eliminates call overhead
for (int i = 0; i < 1000000; i++) {
result += square(i); // No function call per iteration
}

// Enables further optimization
inline int compute(int x) {
return x * 2 + 1;
}

int y = compute(5); // Compiler sees: 5 * 2 + 1
// Can optimize to: 11 (constant folding)

Drawbacks​

// Code bloat
inline void largeFunction() {
// 50 lines of code
}

// Called 100 times = 100 copies of 50 lines
// Increases binary size, hurts instruction cache

Inline Variables (C++17)​

// header.h
inline int global_counter = 0; // OK in header (C++17)

inline const std::string default_name = "Widget";

Before C++17, needed workarounds:

// C++14 workaround
extern const std::string default_name;

// In one .cpp file:
const std::string default_name = "Widget";

Templates and Inline​

Template functions are implicitly inline:

// Implicitly inline (defined in header)
template<typename T>
T max(T a, T b) {
return a > b ? a : b;
}

// Can explicitly mark inline (redundant)
template<typename T>
inline T min(T a, T b) {
return a < b ? a : b;
}

# Modern alternative: LTO inlines across files
g++ -flto -O3 file1.cpp file2.cpp -o app

# Compiler can inline functions in file1.cpp called from file2.cpp
# Without needing 'inline' keyword

Best Practices​

DO
  • Let compiler decide (use -O2/-O3)
  • Mark small, frequently-called functions inline
  • Define inline functions in headers
  • Use for getters/setters
DON'T
  • Inline large functions
  • Inline recursive functions
  • Inline virtual functions (ineffective)
  • Rely on inline for performance (profile first)

Debugging​

Inlined functions don't appear in stack traces:

inline void helper() {
throw std::runtime_error("Error");
}

void process() {
helper(); // Stack trace won't show helper()
}

Solution: Compile with -O0 or use noinline attribute for debugging.


Summary​

inline keyword:

  • Suggests function body replacement
  • Eliminates call overhead
  • Must be defined in header
  • Compilers decide automatically with optimization

Implicit inline:

  • Functions defined in class body
  • constexpr functions
  • Template functions

Modern practice:

// Let compiler decide
int square(int x) { return x * x; }

// Compile with optimization
g++ -O3 program.cpp

// Compiler inlines automatically

Use inline when:

  • Multiple definitions needed (header-only)
  • Defining globals in headers (C++17)
  • Small, performance-critical functions

Key point: Modern compilers are better at inlining decisions than humans. Use optimization flags (-O2, -O3) and profile performance rather than manually adding inline everywhere.