什么时候应该inline
为C ++中的函数/方法编写关键字?
在看到一些答案之后,一些相关的问题:
何时应不写关键字“内联”在C ++函数/方法?
编译器何时不知道何时使函数/方法“内联”?
当一个应用程序为函数/方法写“内联”时,如果一个应用程序是多线程的,这有关系吗?
inline
(9.3 / 2)。
什么时候应该inline
为C ++中的函数/方法编写关键字?
在看到一些答案之后,一些相关的问题:
何时应不写关键字“内联”在C ++函数/方法?
编译器何时不知道何时使函数/方法“内联”?
当一个应用程序为函数/方法写“内联”时,如果一个应用程序是多线程的,这有关系吗?
inline
(9.3 / 2)。
Answers:
哦,老兄,我的宠儿之一。
inline
更像是static
或extern
不是指令告诉编译器内联你的函数。 extern
,static
,inline
是联动指令,几乎完全是由连接器,而不是编译器使用。
据说这inline
向编译器提示您认为应该内联函数。1998年可能确实如此,但是十年后,编译器不需要这些提示。更不用说人类在优化代码方面通常是错误的,因此大多数编译器都会完全忽略“提示”。
static
-变量/函数名称不能在其他翻译单元中使用。链接器需要确保它不会意外使用另一个翻译单元中的静态定义的变量/函数。
extern
-在此翻译单元中使用此变量/函数名称,但如果未定义,请不要抱怨。链接器将对其进行排序,并确保所有尝试使用某个外部符号的代码都有其地址。
inline
-此功能将在多个翻译单元中定义,不用担心。链接器需要确保所有翻译单元都使用变量/函数的单个实例。
注意:通常,声明模板inline
是没有意义的,因为它们已经具有链接语义inline
。但是,需要inline
使用显式的专门化和模板实例化。
您问题的具体答案:
什么时候应该为C ++中的函数/方法编写关键字“内联”?
仅当您希望在标头中定义函数时。仅当函数的定义可以以多个翻译单位显示时才更准确。在头文件中定义小的(如在一个衬里中)函数是一个好主意,因为它为编译器提供了更多信息,可在优化代码时使用。这也增加了编译时间。
什么时候不应该在C ++中为函数/方法编写关键字“内联”?
不要仅仅因为您认为如果编译器将代码内联就可以更快地运行代码,就不要添加内联。
编译器何时不知道何时使函数/方法“内联”?
通常,编译器将比您做得更好。但是,如果编译器没有函数定义,则无法选择内联代码。在最大化优化的代码private
中,无论是否要求,通常所有方法都内联。
另外,要防止在GCC中内联,请使用__attribute__(( noinline ))
,在Visual Studio中请使用__declspec(noinline)
。
当一个应用程序为函数/方法写“内联”时,如果一个应用程序是多线程的,那有关系吗?
多线程不会以任何方式影响内联。
inline
关键字无关。不过,您有个正确的主意。通常,猜测通过内联将改善的内容非常容易出错。该规则的例外是一个班轮。
我想用一个令人信服的示例为这个主题中的所有出色答案做出贡献,以消除所有剩余的误解。
给定两个源文件,例如:
inline111.cpp:
#include <iostream>
void bar();
inline int fun() {
return 111;
}
int main() {
std::cout << "inline111: fun() = " << fun() << ", &fun = " << (void*) &fun;
bar();
}
inline222.cpp:
#include <iostream>
inline int fun() {
return 222;
}
void bar() {
std::cout << "inline222: fun() = " << fun() << ", &fun = " << (void*) &fun;
}
情况A:
编译:
g++ -std=c++11 inline111.cpp inline222.cpp
输出:
inline111: fun() = 111, &fun = 0x4029a0
inline222: fun() = 111, &fun = 0x4029a0
讨论内容:
即使您应该对内联函数有相同的定义,如果不是这种情况,C ++编译器也不会对其进行标记(实际上,由于单独的编译,它无法对其进行检查)。确保这一点是您的责任!
链接器不会抱怨一个定义规则,如fun()
被声明为inline
。但是,由于inline111.cpp是fun()
编译器处理的第一个转换单元(实际上是在调用),因此编译器会在inline111.cpp中实例化fun()
其第一个调用遇到的实例。如果编译器决定不fun()
从程序的任何其他位置(例如,从inline222.cpp)扩展其调用,则对的调用fun()
将始终链接到从inline111.cpp生成的其实例(对inline222.cppfun()
内部的调用)可能还会在该翻译单元中产生一个实例,但它将保持未链接状态)。实际上,从相同的&fun = 0x4029a0
打印输出中可以明显看出这一点。
最后,尽管inline
建议编译器实际扩展单行代码fun()
,但它完全忽略了您的建议,这很明显,因为fun() = 111
在这两行中。
情况B:
编译 (注意反向顺序):
g++ -std=c++11 inline222.cpp inline111.cpp
输出:
inline111: fun() = 222, &fun = 0x402980
inline222: fun() = 222, &fun = 0x402980
讨论内容:
本案例主张案例A中已讨论的内容。
请注意一个重要的观点,如果你注释掉实际调用fun()
在inline222.cpp(如出注释cout
语句来在inline222.cpp完全),那么,尽管你的翻译单元的编译顺序,fun()
将被实例化后,它在第一次调用相遇inline111.cpp,从而使打印出的情况B作为inline111: fun() = 111, &fun = 0x402980
。
案例C:
编译 (注意-O2):
g++ -std=c++11 -O2 inline222.cpp inline111.cpp
要么
g++ -std=c++11 -O2 inline111.cpp inline222.cpp
输出:
inline111: fun() = 111, &fun = 0x402900
inline222: fun() = 222, &fun = 0x402900
讨论内容:
-O2
优化编译鼓励以实实在在地扩大可内联函数(另请注意,-fno-inline
是默认不优化选项)。从此处的输出可以明显看出,fun()
实际上已对in 进行了内联扩展(根据其在特定翻译单元中的定义),从而产生两种不同 fun()
的输出。尽管如此,从相同的打印输出中可以明显看出(标准要求)只有一个全局链接的实例。fun()
&fun
inline
功能变为未定义行为的说明性文章。
.cpp
都是其自己的翻译单元。最好为-flto
启用/禁用添加案例。
1)如今,几乎从来没有。如果内联函数是一个好主意,则编译器将在不需要您帮助的情况下执行该操作。
2)总是。参见#1。
(编辑以反映您将问题分为两个问题...)
inline
仍然需要,例如在头文件中定义一个函数(并且在多个编译单元中内联这样的函数是必需的)。
inline
说明符,则链接器会自动将其实例折叠为一个实例,并且不使用ODR。
什么时候不应该在C ++中为函数/方法编写关键字“内联”?
如果函数是在头部声明和定义.cpp
文件,你应该不写关键字。
编译器何时不知道何时使函数/方法“内联”?
没有这种情况。编译器无法使函数内联。它所能做的就是内联该函数的部分或全部调用。如果没有该函数的代码,它将无法执行此操作(在这种情况下,链接程序必须能够执行此操作)。
当一个应用程序为函数/方法写“内联”时,如果一个应用程序是多线程的,那有关系吗?
不,那没关系。
这取决于所使用的编译器。不要盲目地相信当今的编译器比人类更懂得如何内联,并且出于性能原因,永远不要使用它,因为它是链接指令而不是优化提示。虽然我同意这些论点在意识形态上是正确的,但与现实相遇可能是另一回事。
在阅读了多个线程之后,出于好奇,我尝试了内联对我正在工作的代码的影响,结果是我得到了可测量的GCC加速,而我的Intel编译器没有加速。
(更多详细信息:很少有在类外部定义的关键函数的数学模拟,GCC 4.6.3(g ++ -O3),ICC 13.1.0(icpc -O3);向关键点添加内联会导致GCC代码加速+ 6%)。
因此,如果您将GCC 4.6认定为现代编译器,那么结果是,如果您编写CPU密集型任务并且知道瓶颈在哪里,那么内联指令仍然很重要。
默认情况下,在未启用优化的情况下进行编译时,gcc不会内联任何函数。我不了解Visual Studio – deft_code
我通过使用/ FAcs进行编译并查看汇编代码来检查Visual Studio 9(15.00.30729.01):编译器生成了对成员函数的调用,而未在调试模式下启用优化。即使该函数用__forceinline标记,也不会生成任何内联运行时代码。
您想将其放在返回类型之前的最开始。但是大多数编译器都忽略了它。如果已定义它,并且具有较小的代码块,则大多数编译器无论如何都将其内联。
除非您正在编写库或有特殊原因,否则您可以忘记inline
使用链接时间优化。它消除了必须在标头中包含函数定义的要求,以便可以考虑跨编译单元进行内联,而这正是inline
允许的情况。
(但是请参见是否有任何理由不使用链接时间优化?)
C ++内联与C内联完全不同。
#include <iostream>
extern inline int i[];
int i [5];
struct c {
int function (){return 1;} //implicitly inline
static inline int j = 3; //explicitly inline
};
int main() {
c j;
std::cout << i;
}
inline
它本身会影响编译器,汇编器和链接器。这是对编译器的指令,如果在转换单元中使用了该函数/数据,则仅发出该函数/数据的符号;如果是,则类似于类方法,告诉汇编器将其存储在该节中.section .text.c::function(),"axG",@progbits,c::function(),comdat
或.section .bss.i,"awG",@nobits,i,comdat
用于数据。
这是.section name, "flags"MG, @type, entsize, GroupName[, linkage]
。例如,部分名称为.text.c::function()
。axG
表示该节是可分配的,可执行的,并且在一个组中,即将指定一个组名(并且没有M标志,因此将不指定entsize);@progbits
表示该部分包含数据且不为空;c::function()
是组名,并且该组具有comdat
链接意味着在所有目标文件中,带有此组名并用comdat标记的所有节都将从最终可执行文件中删除,除了1之外,即编译器确保翻译单元中只有一个定义,然后告诉汇编器放入将其放在目标文件中自己的组中(1组中的1个部分),然后链接器将确保,如果任何目标文件具有相同名称的组,则在最终.exe中仅包含一个。汇编器和链接器现在可以看到inline
与不使用之间的区别inline
,因为链接器因为它们的指令未将其存储在常规文件.data
或.text
其他文件中。
static inline
在类中,这意味着它是类型定义而不是声明(允许在类中定义静态成员)并使它内联;现在,它的行为如上所述。
static inline
at文件作用域仅影响编译器。这对编译器意味着:仅在此函数/数据在转换单元中使用时才发出此符号,并作为常规静态符号(不带.globl指令存储在.text /.data中)发出此符号。对于汇编程序,现在static
和之间没有区别static inline
extern inline
是一个声明,表示您必须在翻译单元中定义此符号或抛出编译器错误;如果已定义,则将其视为常规对象inline
,对于汇编器和链接器,extern inline
和之间没有区别inline
,因此,这仅是编译器保护。
extern inline int i[];
extern int i[]; //allowed repetition of declaration with incomplete type, inherits inline property
extern int i[5]; //declaration now has complete type
extern int i[5]; //allowed redeclaration if it is the same complete type or has not yet been completed
extern int i[6]; //error, redeclaration with different complete type
int i[5]; //definition, must have complete type and same complete type as the declaration if there is a declaration with a complete type
上面的所有内容(没有错误行)都折叠为inline int i[5]
。显然,如果这样做了,extern inline int i[] = {5};
则extern
由于通过赋值进行了明确的定义,因此将被忽略。
在开发和调试代码时,请省去inline
。它使调试复杂化。
添加它们的主要原因是为了帮助优化生成的代码。通常,这是以增加的代码空间为代价的,但是有时却inline
节省了代码空间和执行时间。
在算法完成之前展开这种关于性能优化的想法就是过早的优化。
inline
除非使用优化进行编译,否则通常不会内联函数,因此它们不会以任何方式影响调试。请记住,这只是一个提示,而不是需求。
inline
内联了函数。在其中设置有意义的断点是不可能的。
inline
将无助于改进现代编译器上的代码,现代编译器可以确定是否单独内联。
什么时候应该内联:
1.当要避免函数调用时发生的事情开销如参数传递,控制传递,控制返回等。
2.函数应该小,经常调用,并且内联实际上是有优势的,因为按照80-20的规则,尝试使那些对程序性能有重大影响的函数内联。
众所周知,内联只是向编译器发出的类似于注册的请求,这将使您花费目标代码大小。
inline
已经失去了其作为优化提示的地位,并且大多数编译器仅将其用于允许多个定义-如IMO所愿。更重要的是,自C ++ 11起,由于register
其“我比编译器更懂得如何优化”的先前含义而被完全弃用:它现在只是一个保留字,没有当前含义。
inline
在某种程度上收听。
C ++内联函数是类中常用的强大概念。如果函数是内联函数,则编译器将在编译时调用该函数的每个点上放置该函数代码的副本。
对内联函数的任何更改都可能需要重新编译该函数的所有客户端,因为编译器将需要再次替换所有代码,否则它将继续使用旧功能。
要内联函数,请将关键字inline放在函数名称之前,并在对函数进行任何调用之前定义函数。如果定义的函数超过一行,则编译器可以忽略内联修饰符。
即使不使用内联说明符,类定义中的函数定义也是内联函数定义。
下面是一个示例,该示例利用内联函数返回两个数的最大值
#include <iostream>
using namespace std;
inline int Max(int x, int y) { return (x > y)? x : y; }
// Main function for the program
int main() {
cout << "Max (100,1010): " << Max(100,1010) << endl;
return 0;
}
有关更多信息,请参见此处。