c++ - 内联扩展编译成函数调用 [c++]
问题描述
介绍:
我一直在创建一个简单的包装类。我随机发现(或者看起来是)一个内联函数仍然编译成一个函数调用。我创建了一个示例类来测试事情,这就是我发现的:
考虑以下类:
//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;}
};
这个类有3个功能供参考。
- GetInt函数是一个标准函数。
- GetInt_Inl函数是一个内联函数。
- 在编译器决定不将GetInt_Inl实现为内联函数的情况下, GetInt_ForceInl函数是一个确保的内联函数
像这样实现:
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
myInt 设置的结果汇编代码(取自反汇编程序):
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
如上所示,直接来自InlineTestClass成员的(myInt)设置是(如预期的那样) 2 mov 指令长。从GetInt函数设置会导致函数调用(如预期的那样),但是 GetInt_Inl和GetInt_ForceInl(内联函数)也会导致函数调用。
似乎内联函数已被编译为完全忽略内联的普通函数(如果我错了,请纠正我)。
根据MSVC 文档,这是一个奇怪的原因:
inline 和 __inline 说明符指示编译器将函数体的副本插入到调用函数的每个位置。
这(我认为)会导致:
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
这意味着汇编代码也应该与直接从类成员设置的代码相同,但事实并非如此。
问题:
- 我是否遗漏了某些东西或错误地执行了这些功能?
- 我在解释 inline 关键字的功能吗?它是什么?
- 为什么这些内联函数会导致函数调用?
解决方案
默认情况下,类中定义的函数是“推荐内联”的。所以 inline 绝对什么都不做。此外,编译器始终可以随意否决程序员的关键字。这只是建议性的。
来自 C++17 草案(第 147 页):
inline 说明符向实现表明,在调用点对函数体进行内联替换优于通常的函数调用机制。在调用点执行此内联替换不需要实现;然而,即使省略了这个内联替换,本小节中规定的内联函数的其他规则仍应得到遵守。
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4713.pdf
推荐阅读
- javascript - 点击(不释放)或点击javascript中的事件?
- python - 如何在 IBM Cloud 函数中运行 docker 映像?
- excel - 应税销售额计算(税表)
- python - 如何修复'FileNotFoundError' ... parser_f 中的parsers.py?
- angular - Angular ng-multiselect-dropdown 填充对象数组
- spring-boot - 我可以将文件从本地目录上传到在 openshift 上运行的程序吗?
- html - 主导航开始滚动,然后返回
- java - 如何更改列表的日期格式
- dji-sdk - Onboard-SDK的控制命令发布频率不同
- php - mysqli_query 仅在有 WHERE 语句时返回结果