Inline functions —— what a wonderful idea! They look like functions, they act like functions, they are ever so much better than macros, and you cal call them without having to incur the overhead of a function call.
In programming, however, there is no free lunch, and inline functions are no exception. The idea behind inline function is to replace each call of that function with its code body, and it absolutely increase the size of the object code. On machines with limited memory, overzealous inlining can give rise to programs that are too big for the available space. Even with virtual memory, inline-induced code bloat can lead to additional paging, a reduced instruction cache hit rate, and the performance penalties that accompany these things.
On the other hand, if an inline function body is very short, the code generated for the function body may be smaller than the code generated for a function call. It that is the case, inlining the function may actually lead to smaller object code and a higher instruction cache hit rate!
Bear in mind that inline
is a request to compilers, not a command. The request can be given implicitly or explicitly.
The implicitly way is to define a function inside a class definition :
class Person{
public:
int age() const {return theAge;}
private:
int theAge;
};
The explicitly way to declare an inline function is to precede its definition with the inline
keyword. For example, this is how the standard max
template :
template<typename T>
inline const T& std::max(const T& a, const T& b)
{
return a < b? b : a;
}
The fact that max
is a template brings up the observation that both inline functions and templates are typically defined in header files. This lead some programmers to conclude that function templates must be inline. This conclusion is both invalid and potentially harmful.
Inline functions must typically be in header files, because most build environments do inlining during compilation. In order to replace a function call with the body of the called function, compilers must know what the function looks like.
Templates are typically in header files, because compilers need to know what a template looks like in order to instantiate it when it is used.
Template instantiation is independent of inlining.
Let us focus on the observation that inline
is a request that compilers may ignore. Most compilers refuse to inline functions they deem too complicated, and all but the most trivial calls to virtual functions defy inlining. virtual
means "wait until runtime to figure out which function to call", and inline
means "before execution, replace the call site with the called function".
It all adds up to this: whether a given inline function is actually inlined depends on the build environment you are using -- primarily on the compiler. Fortunately, most compilers have a diagnostic level that will result in a warning if they fail to inline a function you have asked them to.
Sometimes compilers generate a function body for an inline function even when they are perfectly willing to inline the function. For example, if your program takes the address of an inline function, compilers must typically generate an outlined function body for it.
How can they come up with a pointer to a function that does not exists?
inline void f(){...} // assume compilers are willing to inline calls to f
void (*pf) () = f;
...
f(); // this call will be inlined, because it is a normal call
pf(); // this call probably won't be, because it is through a function pointer
Library designers must evaluate the impact of declaring functions inline
, because it is impossible to provide binary upgrades to the client visible inline functions in a library. In other words, if f
is an inline function in a library, clients of the library compile the body of f
into their applications. If a library implementer later decides to change f
, all clients who have used f
must recompile. This is often undesirable. On the other hand, if f
is a non-inline function, a modification to f
requires only that clients relink. This is a substantially less onerous burden than recompiling and , if the library containing the function is dynamically linked, that may be absorbed in a way that's completely transparent to clients.
From a practical point of view during coding, most debuggers have trouble with inline functions. How do you set a breakpoint in a function that is not there ?
Conclusion
- Limit most inlining to small, frequently called functions. This facilitates debugging and binary upgradability, minimizes potential code bloat, and maximizes the chances of greater program speed.
- Don't declare function templates
inline
just because they appear in header files.