C++ inline Functions: ODR, Headers, and Compiler Inlining

C++ inline Functions: ODR, Headers, and Compiler Inlining

이 글의 핵심

Practical guide to inline in C++: header-safe definitions, difference from actual inlining, and C++17 inline variables.

What does “inline function” mean?

Historically: “replace the call with the body.” In modern C++, inline is also how you legally define the same function in multiple translation units (ODR).

// Ordinary function (one definition per program)
int add(int a, int b) {
    return a + b;
}

// inline function: can live in a header
inline int add_inline(int a, int b) {
    return a + b;
}

int main() {
    int x = add_inline(3, 4);
    // compiler may inline the body: int x = 3 + 4;
}

Why use inline?

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

for (int i = 0; i < 1000000; i++) {
    int result = square(i);  // call may be inlined
}

Defining in headers

// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H

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

inline int multiply(int a, int b) {
    return a * b;
}

#endif

// main.cpp
#include "math_utils.h"

int main() {
    int x = add(3, 4);
    int y = multiply(5, 6);
}

Class member functions

class Point {
private:
    int x, y;
    
public:
    Point(int x, int y) : x(x), y(y) {}
    
    // Defined in class: implicitly inline
    int getX() const {
        return x;
    }
    
    int getY() const {
        return y;
    }
    
    inline void setX(int newX) {
        x = newX;
    }
};

// Out-of-line definition still needs inline if in header
inline void Point::setY(int newY) {
    y = newY;
}

Practical examples

Example 1: Getters / setters

class Rectangle {
private:
    int width, height;
    
public:
    Rectangle(int w, int h) : width(w), height(h) {}
    
    int getWidth() const { return width; }
    int getHeight() const { return height; }
    
    void setWidth(int w) { width = w; }
    void setHeight(int h) { height = h; }
    
    int area() const { return width * height; }
};

Example 2: Math helpers

inline int abs(int x) {
    return x < 0 ? -x : x;
}

inline int max(int a, int b) {
    return a > b ? a : b;
}

inline int min(int a, int b) {
    return a < b ? a : b;
}

inline int clamp(int value, int low, int high) {
    return max(low, min(value, high));
}

int main() {
    int x = clamp(150, 0, 100);  // 100
    std::cout << x << std::endl;
}

Example 3: Small vector type

struct Vec2 {
    float x, y;
    
    Vec2(float x = 0, float y = 0) : x(x), y(y) {}
    
    inline Vec2 operator+(const Vec2& other) const {
        return Vec2(x + other.x, y + other.y);
    }
    
    inline Vec2 operator-(const Vec2& other) const {
        return Vec2(x - other.x, y - other.y);
    }
    
    inline Vec2 operator*(float scalar) const {
        return Vec2(x * scalar, y * scalar);
    }
    
    inline float dot(const Vec2& other) const {
        return x * other.x + y * other.y;
    }
    
    inline float length() const {
        return std::sqrt(x * x + y * y);
    }
};

int main() {
    Vec2 v1(1, 2);
    Vec2 v2(3, 4);
    
    Vec2 v3 = v1 + v2;
    float d = v1.dot(v2);
    float len = v1.length();
}

Example 4: Bit helpers

inline bool getBit(int value, int pos) {
    return (value & (1 << pos)) != 0;
}

inline int setBit(int value, int pos) {
    return value | (1 << pos);
}

inline int clearBit(int value, int pos) {
    return value & ~(1 << pos);
}

inline int toggleBit(int value, int pos) {
    return value ^ (1 << pos);
}

int main() {
    int flags = 0;
    
    flags = setBit(flags, 0);
    flags = setBit(flags, 2);
    flags = clearBit(flags, 0);
    
    std::cout << getBit(flags, 2) << std::endl;  // 1
}

Limitations

// inline is a hint; compiler may refuse
inline void complexFunction() {
    // large body
}

// Often not inlined:
// - deep recursion
// - very large functions
// - virtual calls (usually)
// - calls through function pointers

Common pitfalls

Pitfall 1: Inlining huge functions

// Bad: huge inline
inline void hugeFunction() {
    // hundreds of lines
}

// Good: keep small helpers inline
inline int add(int a, int b) {
    return a + b;
}

Pitfall 2: Recursive functions

inline int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}
// Usually not fully inlined

Pitfall 3: Duplicate definitions in headers

// utils.h
int add(int a, int b) {  // ODR violation if included in multiple TUs
    return a + b;
}

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

Pitfall 4: Virtual functions

class Base {
public:
    virtual inline void func() {
        // usually not inlined when called virtually
    }
};

Base obj;
obj.func();  // may inline (static type known)

Base* ptr = &obj;
ptr->func();  // usually not inlined

Modern C++: inline

// C++17: inline variable
inline int globalCounter = 0;  // OK in header

class MyClass {
public:
    inline static int count = 0;  // C++17
};

Guidelines

// ✅ Good inline candidates
inline int getValue() const { return value; }

inline int max(int a, int b) { return a > b ? a : b; }

template<typename T>
inline T square(T x) { return x * x; }

// ❌ Often unnecessary
// - huge functions
// - recursion
// - compiler already inlines small hot functions

FAQ

Q1: When to use inline?

A:

  • Small hot functions
  • Getters/setters
  • Header-only definitions

Q2: Performance?

A: Can remove call overhead for tiny functions—measure.

Q3: Is inline mandatory for performance?

A: No—it is a hint; the optimizer decides.

Q4: Header definitions?

A: Non-template functions in headers generally need inline (or templates).

Q5: Compiler auto-inline?

A: Yes, with optimization flags.

Q6: Learning resources?

A:

  • Effective C++
  • cppreference.com
  • C++ Primer

  • RVO/NRVO
  • Expression templates
  • Profiling

Practical tips

Debugging

  • Warnings first

Performance

  • Profile first

Code review

  • Conventions

Practical checklist

Before coding

  • Right approach?
  • Maintainable?
  • Performance?

While coding

  • Warnings?
  • Edge cases?
  • Errors?

At review

  • Intent?
  • Tests?
  • Docs?

Keywords

C++, inline, optimization, ODR, header


  • RVO/NRVO
  • Alignment & padding
  • Branch prediction
  • Cache optimization
  • Copy elision