Jump to content

Inline function

From Wikipedia, the free encyclopedia

This is an old revision of this page, as edited by André Neves (talk | contribs) at 04:37, 18 October 2015 (corrected link). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

In the C and C++ programming languages, an inline function is one qualified with the keyword inline; this serves two purposes. Firstly, it serves as a compiler directive that suggests (but does not require) that the compiler substitute the body of the function inline by performing inline expansion, i.e.: by inserting the function code at the address of each function call, thereby saving the overhead of a function call. In this respect it is analogous to the register storage class specifier, which similarly provides an optimization hint.[1] The second purpose of inline is to change linkage behavior; the details of this are complicated. This is necessary due to the C/C++ separate compilation + linkage model, specifically because the definition (body) of the function must be duplicated in all translation units where it is used, to allow inlining during compiling, which, if the function has external linkage, causes a collision during linking (it violates uniqueness of external symbols; in C++, the One Definition Rule). C and C++ (and dialects such as GNU C and Visual C++) resolve this in different ways.[1]

Problems

Besides the problems with inline expansion in general, inline functions as a language feature may not be as valuable as they appear, for a number of reasons:

  • Often, a compiler is in a better position than a human to decide whether a particular function should be inlined. Sometimes the compiler may not be able to inline as many functions as the programmer indicates.
  • An important point to note is that the code (of the inline function) gets exposed to its client (the calling function).
  • As functions evolve, they may become suitable for inlining where they were not before, or no longer suitable for inlining where they were before. While inlining or un-inlining a function is easier than converting to and from macros, it still requires extra maintenance which typically yields relatively little benefit.
  • Inline functions used in proliferation in native C-based compilation systems can increase compilation time, since the intermediate representation of their bodies is copied into each call site where they are
  • The specification of inline in C99 requires exactly one additional external definition of a function in another compilation unit, when the corresponding inline definition, that can occur multiple times in different compilation units, if that function is used somewhere. That can easily lead to linker errors because such a definition wasn't provided by the programmer. For this reason, inline in C99 often is used together with static, which gives the function internal linkage.
  • In C++, it is necessary to define an inline function in every module (compilation unit) that uses it, whereas an ordinary function must be defined in only a single module. Otherwise it would not be possible to compile a single module independently of all other modules.

Language support

C++, C99, and GNU C each have support for inline functions. Different compilers vary in how complex a function they can manage to inline. Mainstream C++ compilers like Microsoft Visual C++ and GCC support an option that lets the compilers automatically inline any suitable function, even those not marked as inline functions.

An inline function can be written in C++ or C++11 like this:

inline void swap(int & m, int & n)
{
  int temp = m;
  m = n;
  n = temp;
}

Then, a statement such as the following:

swap(x, y);

will be translated into:

int temp = x;
x = y;
y = temp;

When implementing a sorting algorithm doing lots of swaps, this speeds things up a lot.

On Older Compilers

A side effect that has now been eliminated on the newer compilers is that functions passing by value used to lose their encapsulation and behave as if they were passing by reference.

For example, consider the function

inline int f(int n){n = n + 2; return n;}

Using this in the code

int x = 10;
cout<<x<<endl;
int y = f(x);
cout<<y<<endl;
cout<<x<<endl;

without the inline, would print out

10
12
10

But with the inline it will translate into

int x = 10;
cout<<x<<endl;
x = x + 2;
int y = x;
cout<<y<<endl;
cout<<x<<endl;

which will print out

10
12
12

This is exactly what the function

int f(int & n){n = n + 2; return n;}

would print out in the example without the inline. So, in effect, it passes by reference.

Remedy: Inserting a Cutout

There is an easy remedy for the side effect: We put the parameter in a location temp and apply the function to temp. The variable temp will act like a cutout in a spy novel. For example, with our function

inline int f(int n){n = n + 2; return n;}

The code calling it was

int x = 10;
cout<<x<<endl;
int y = f(x);
cout<<y<<endl;
cout<<x<<endl;

We simply modify this code to

int x = 10;
cout<<x<<endl;
int temp = x;
int y = f(temp);
cout<<y<<endl;
cout<<x<<endl;

and it will print out

10
12
10

because the parameter x is not modified.[2] Probably the newer compilers do something similar to eliminate the side effect.

Microsoft Visual C++ specific

Microsoft Visual C++ and few other compilers support non-standard constructs for defining inline functions, such as __inline and __forceinline specifiers.

  • The __inline keyword is equivalent to inline.
  • The __forceinline keyword allows the programmer to force the compiler to inline the function, but indiscriminate use of __forceinline can result in larger code (bloated executable file), minimal or no performance gain, and in some cases even a loss in performance. The compiler cannot inline the function in all circumstances, even with the __forceinline keyword applied. If the compiler cannot inline a function declared with __forceinline, a warning of level 1 is generated. A list of cases when __forceinline will not take effect is listed below (based on Microsoft Specifications at MSDN):
  1. The function or its caller is compiled with /Ob0 (the default option for debug builds).
  2. The function and the caller use different types of exception handling (C++ exception handling in one, structured exception handling in the other).
  3. The function has a variable argument list.
  4. The function uses inline assembly, unless compiled with /Og, /Ox, /O1, or /O2.
  5. The function is recursive and not accompanied by #pragma inline_recursion(on). With the pragma, recursive functions are inlined to a default depth of 16 calls. To reduce the inlining depth, use inline_depth pragma.
  6. The function is virtual and is called virtually. Direct calls to virtual functions can be inlined.
  7. The program takes the address of the function and the call is made via the pointer to the function. Direct calls to functions that have had their address taken can be inlined.
  8. The function is also marked with the naked __declspec modifier.

__forceinline is useful if:

  • inline or __inline is not respected by the compiler (ignored by compiler cost/benefit analyzer)
  • code portability is not required
  • inlining results in a necessary performance boost

Example of portable code:

#ifdef _MSC_VER
  #define INLINE __forceinline /* use __forceinline (VC++ specific) */
#else
  #define INLINE inline        /* use standard inline */
#endif

INLINE void foo() { /* inline function body */ }

Quotes

"A function declaration [ . . . ] with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by 7.1.2 shall still be respected."
— ISO/IEC 14882:2011, the current C++ standard, section 7.1.2
"A function declared with an inline function specifier is an inline function. [ . . . ] Making a function an inline function suggests that calls to the function be as fast as possible. The extent to which such suggestions are effective is implementation-defined (footnote: For example, an implementation might never perform inline substitution, or might only perform inline substitutions to calls in the scope of an inline declaration.)
"[ . . . ] An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition."
— ISO 9899:1999(E), the C99 standard, section 6.7.4

See also

References

  1. ^ a b Meyers, Randy (July 1, 2002). "The New C: Inline Functions". {{cite journal}}: Cite journal requires |journal= (help)
  2. ^ Mastering Object-Oriented Design in C++, Cay S. Horstmann, John Wiley & Sons, p.46 et seq, (1995)