# C++: inline function

Yao Yao on March 30, 2015
• Published in category
• C++

## 1. Why inline function?

P415~420 讲的是函数式 #define 有 efficiency 的优势，同时有很多缺陷；而 inline function 是为了弥补这些缺陷同时保留 efficiency 的优势。

• pushing arguments
• making an assembly-language CALL
• returning arguments
• performing an assembly-language RETURN

• 非常容易出错，比如复杂参数代入后的优先级问题，i++ 这样的有 side effect 的参数代入后会被执行多次
• The preprocessor has no permission to access class member data. This means preprocessor macros cannot be used as class member functions.

Inline function, which is a true function in every sense. Any behavior you expect from an ordinary function, you get from an inline function. The only difference is that an inline function is expanded in place, like a preprocessor macro, so the overhead of the function call is eliminated. Thus, you should (almost) never use macro functions, only inline functions.

## 2. How inline function?

Any member function defined within a class body, a.k.a in situ function, is automatically inline.

class Foo {
public:
void method();  		// Best practice: Don't put the inline keyword here
// ...
};

inline void Foo::method() { // Best practice: Put the inline keyword here
// ...
}


• .h: class declaration + inline member function 实现
• .cpp: 其他 non-inline member function 实现

If all your functions are inlined, using the library becomes quite simple because there’s no linking necessary. However, inlining a big function will cause that code to be duplicated everywhere the function is called, producing code bloat that may mitigate ([ˈmɪtɪgeɪt], To reduce, lessen, or decrease) the speed benefit. The only reliable course of action is to experiment to discover the effects of inlining on your program with your compiler.

## 3. Access functions

One of the most important uses of inlines inside classes is the access function.

Access function 说白了就是 getter 和 setter，C++ 界也有叫 accessors 和 mutators 的。举个例子：

class Access {
int i;
public:
int read() const { return i; } // 直接在 class 内部定义的 function 默认是 inline
void set(int ii) { i = ii; }
};

int main() {
Access A;
A.set(100);
}


## 4. Inlines and the compiler

To understand when inlining is effective, it’s helpful to know what the compiler does when it encounters an inline. As with any function, the compiler holds the function type (that is, the function prototype including the name and argument types, in combination with the function return value) in its symbol table. In addition, when the compiler sees that the inline’s function type and the function body parses without error, the code for the function body is also brought into the symbol table. Whether the code is stored in source form, compiled assembly instructions, or some other representation is up to the compiler.

When you make a call to an inline function, the compiler first ensures that the call can be correctly made. That is, all the argument types must either be the exact types in the function’s argument list, or the compiler must be able to make a type conversion to the proper types and the return value must be the correct type (or convertible to the correct type) in the destination expression. This, of course, is exactly what the compiler does for any function and is markedly different from what the preprocessor does because the preprocessor cannot check types or make conversions.

If all the function type information fits the context of the call, then the inline code is substituted directly for the function call, eliminating the call overhead and allowing for further optimizations by the compiler. Also, if the inline is a member function, the address of the object (this) is put in the appropriate place(s), which of course is another action the preprocessor is unable to perform.

### Limitations

There are two situations in which the compiler cannot perform inlining. In these cases, it simply reverts to the ordinary form of a function by taking the inline definition and creating storage for the function just as it does for a non-inline. If it must do this in multiple translation units (which would normally cause a multiple definition error), the linker is told to ignore the multiple definitions.

The compiler cannot perform inlining if the function is too complicated. This depends upon the particular compiler, but at the point most compilers give up, the inline probably wouldn’t gain you any efficiency. In general, any sort of looping is considered too complicated to expand as an inline, and if you think about it, looping probably entails much more time inside the function than what is required for the function call overhead.

The compiler also cannot perform inlining if the address of the function is taken implicitly or explicitly. If the compiler must produce an address, then it will allocate storage for the function code and use the resulting address.

It is important to understand that an inline is just a suggestion to the compiler; the compiler is not forced to inline anything at all. A good compiler will inline small, simple functions while intelligently ignoring inlines that are too complicated.

### Forwarding a reference to another function in an inline function is OK

class Forward {
int i;
public:
Forward() : i(0) {}

// Call to undeclared function:
int f() const { return g() + 1; }

int g() const { return i; }
};

int main() {
Forward frwd;
frwd.f();
}


In f(), a call is made to g() (这里 g() 是否是 inline 并不影响), although g() has not yet been declared. This works because the language definition states that no inline functions in a class shall be evaluated until the closing brace of the class declaration.