-2

Introduction:

I have been creating a simple wrapper classes. I randomly found out that (or it appears to be) an inline function still compiled into a function call. I created an example class to test things out and this is what I found:

Consider the following class:

//compile with MSVC class InlineTestClass { public: int InternalInt; int GetInt() {return InternalInt;} inline int GetInt_Inl() {return InternalInt;} //__forceinline -Forces the compiler to implement the function as inline __forceinline int GetInt_ForceInl() {return InternalInt;} }; 

This class has 3 functions for reference.

  • The GetInt function is a standard function.
  • The GetInt_Inl function is an inline function.
  • The GetInt_ForceInl function is an ensured inline function in case of the compiler deciding not to implement GetInt_Inl as inline function

Implemented like so:

InlineTestClass itc; itc.InternalInt = 3; int myInt; myInt = itc.InternalInt; //No function myInt = itc.GetInt(); //Normal function myInt = itc.GetInt_Inl(); //Inline function myInt = itc.GetInt_ForceInl(); //Forced inline function 

The resulting assembler code of the setting of myInt (taken from dissassembler):

 451 myInt = itc.InternalInt; 0x7ff6fe0d4cae <+0x003e> mov eax,dword ptr [rsp+20h] 0x7ff6fe0d4cb2 <+0x0042> mov dword ptr [rsp+38h],eax 452 myInt = itc.GetInt(); 0x7ff6fe0d4cb6 <+0x0046> lea rcx,[rsp+20h] 0x7ff6fe0d4cbb <+0x004b> call nD_Render!ILT+2125(?GetIntInlineTestClassQEAAHXZ) (00007ff6`fe0d1852) 0x7ff6fe0d4cc0 <+0x0050> mov dword ptr [rsp+38h],eax 453 myInt = itc.GetInt_Inl(); 0x7ff6fe0d4cc4 <+0x0054> lea rcx,[rsp+20h] 0x7ff6fe0d4cc9 <+0x0059> call nD_Render!ILT+1885(?GetInt_InlInlineTestClassQEAAHXZ) (00007ff6`fe0d1762) 0x7ff6fe0d4cce <+0x005e> mov dword ptr [rsp+38h],eax 454 myInt = itc.GetInt_ForceInl(); 0x7ff6fe0d4cd2 <+0x0062> lea rcx,[rsp+20h] 0x7ff6fe0d4cd7 <+0x0067> call nD_Render!ILT+715(?GetInt_ForceInlInlineTestClassQEAAHXZ) (00007ff6`fe0d12d0) 0x7ff6fe0d4cdc <+0x006c> mov dword ptr [rsp+38h],eax 

As shown above the setting (of myInt) from the member of InlineTestClass directly is (as expected) 2 mov instructions long. Setting from the GetInt function results in a function call (as expected), however both of the GetInt_Inl and GetInt_ForceInl (inline functions) also result in a function call.

It appears as if the inline function has been compiled as a normal function ignoring the inlining completely (correct me if I am wrong).

This is strange cause according to MSVC documentation:

The inline and __inline specifiers instruct the compiler to insert a copy of the function body into each place the function is called.

Which (I think) would result in:

inline int GetInt_Inl() {return InternalInt; //Is the function body} myInt = itc.GetInt_Inl(); //Call site //Should result in myInt = itc.InternalInt; //Identical to setting from the member directly 

Which means that the assembler code should be also identical to the one of setting from the class member directly but it isn't.

Questions:

  1. Am I missing something or implementing the functions incorrectly?
  2. Am I interpreting the function of the inline keyword? What is it?
  3. Why do these inline functions result in a function call?
5
  • 4
    if you compile without optimization functions will be never inlined. begin from change compiler options. The function is compiled by using /Od or /Ob0. No inlining is expected in these cases. can assume that /Od is on if look on your code Commented Sep 28, 2018 at 21:50
  • 2
    You forgot to read the next paragraph: The insertion (called inline expansion or inlining) occurs only if the compiler's cost/benefit analysis show it to be profitable. Inline expansion alleviates the function-call overhead at the potential cost of larger code size.. inline is only a suggestion that the function be inlined. Commented Sep 28, 2018 at 21:54
  • 1
    additionally: The compiler treats the inline expansion options and keywords as suggestions. There is no guarantee that functions will be inlined. You cannot force the compiler to inline a particular function, even with the __forceinline keyword. Commented Sep 28, 2018 at 21:54
  • Also: Why should I ever use inline code? Commented Sep 28, 2018 at 22:41
  • And note that if you do a debug build MSVC will not inline regardless. (Non-inline is generally far easier to walk through with the debugger). Commented Sep 28, 2018 at 22:51

1 Answer 1

2

Functions defined within classes are 'recommended inline' by default. So inline there does absolutely nothing. Also, the compiler is always free to overrule the programmer's keyword regardless. It's merely advisory.

From the C++17 draft (page 147):

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 specified in this subclause shall still be respected.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4713.pdf

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.