Inline Functions in C++: What the inline Keyword Really Does
Almost every tutorial tells you inline makes functions faster by pasting the body at the call site. That was true in 1995. Today it’s misleading, and believing it leads people to sprinkle inline everywhere for no benefit.
Here is what inline actually does in modern C++, and the one situation where you genuinely need it.
The Original Idea: Avoiding Call Overhead
Calling a function isn’t free. The CPU pushes arguments, jumps to another address, runs the body, and jumps back. For a tiny function inside a hot loop, that bookkeeping can cost more than the work.
Inlining means the compiler replaces the call with a copy of the body:
inline int square(int x) {
return x * x;
}
int main() {
int result = square(5); // compiler may emit: int result = 5 * 5;
}
No jump, no return, and the compiler can now fold 5 * 5 into a constant at compile time. That’s a real optimisation — it’s just not one you control with the keyword.
The Keyword Is a Request, Not a Command
This is the part that surprises people: the compiler is free to ignore inline completely.
Compilers have far better information than you do — they know the function’s size, how often it’s called, and whether inlining would blow up the instruction cache. So in practice:
- Functions you mark
inlineoften are not inlined (too big, recursive, address taken). - Functions you never mark are inlined all the time, especially with
-O2and link-time optimisation.
If you compile the square example with g++ -O2, it gets inlined whether or not you write the keyword. Remove the keyword and the generated assembly is identical.
What inline Is Actually For Today
The real, non-optional job of inline is satisfying the One Definition Rule: a program may contain only one definition of any given function.
Say you put a helper in a header:
// mathutils.h
#pragma once
int square(int x) { // definition in a header — trouble ahead
return x * x;
}
Now two source files include it:
// main.cpp
#include "mathutils.h"
int main() { return square(4); }
// helper.cpp
#include "mathutils.h"
int helper() { return square(9); }
Each .cpp compiles fine on its own, but the linker then sees square defined twice and stops:
multiple definition of `square(int)'
The include guard doesn’t help — it only prevents double inclusion within one translation unit, not across two separate ones.
Add one keyword and the error disappears:
// mathutils.h
#pragma once
inline int square(int x) {
return x * x;
}
inline tells the linker “you will see this definition more than once, that’s intentional, keep one and discard the rest.” That is the reason to type it.
Member Functions Defined Inside a Class Are Already Inline
You don’t need the keyword here:
class Circle {
double radius;
public:
Circle(double r) : radius(r) {}
double area() const { // implicitly inline
return 3.14159 * radius * radius;
}
};
Any function defined inside the class body is implicitly inline, which is why headers full of small class methods link without complaint. You only need the explicit keyword when you define a member function outside the class, in a header:
// circle.h
class Circle {
double radius;
public:
double area() const;
};
inline double Circle::area() const { // inline needed here
return 3.14159 * radius * radius;
}
inline vs #define Macros
Before inline, people used macros to avoid call overhead. Macros are text substitution and they bite:
#define SQUARE(x) ((x) * (x))
int i = 5;
int bad = SQUARE(i++); // expands to ((i++) * (i++)) — undefined behaviour
i++ runs twice. An inline function evaluates its argument exactly once:
inline int square(int x) { return x * x; }
int i = 5;
int good = square(i++); // fine — i incremented once, 25 computed
Inline functions also respect types, scope, and namespaces, and debuggers can step into them. Prefer them to function-like macros in every case.
Practical Guidelines
- Do mark free functions
inlinewhen you define them in a header. - Do define small functions in headers when you want them available for inlining across files.
- Don’t add
inlinehoping for speed — measure first, and trust-O2over the keyword. - Don’t mark large functions
inline; if the compiler obeys, you get code bloat and worse cache behaviour. - Consider
constexprinstead when the value can be computed at compile time —constexprfunctions are implicitlyinlinetoo.
The short version: think of inline as a linkage keyword that happens to have “inline” in the name.
Related Articles
- C++ Functions Tutorial: How to Write and Use Functions
- C++ Header Files Explained
- C++ Preprocessor Directives Explained
- Undefined Reference and Linker Errors in C++
- C++ Function Overloading Explained
Take Your C++ Further
If you’re looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you’re a complete beginner or looking to solidify your understanding. Just $19.