何时应该在函数/方法中使用关键字'inline'?
何时应该在函数/方法中使用关键字'inline'?
在C++中,何时应该为函数/方法编写关键字inline
?
看到一些答案后,还有一些相关问题:
- 在C++中,何时不应该为函数/方法编写关键字\'inline\'?
- 当编写函数/方法时,编译器何时无法知道何时使其\'inline\'?
- 如果一个应用程序在编写函数/方法时使用\'inline\',那么它是否与多线程相关?
我想通过一个令人信服的例子来贡献这个帖子中所有伟大答案,以消除任何仍然存在的误解。
给出两个源文件,例如:
-
inline111.cpp:
#include
void bar(); inline int fun() { return 111; } int main() { std::cout << "inline111: fun() = " << fun() << ", &fun = " << (void*) &fun; bar(); } -
inline222.cpp:
#include
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()
),编译程序会在其首次调用遇到fun()
时实例化。如果编译器决定不展开代码从程序的任何其他地方(例如,来自inline222.cpp
),则对fun()
的调用将始终链接到来自inline111.cpp
的实例( inline222.cpp 中的对fun()
的调用也可以在该翻译单位中产生一个实例,但它将保持未链接)。的确,从相同的& fun = 0x4029a0
打印输出可以看出来。 -
最后,尽管C ++编译器将
inline
展开成单行的fun()
,但它完全忽略了您的建议,这是因为这两个行中都是fun()= 111
。
-
-
情况B:
编译(请注意反向顺序):
g++ -std=c++11 inline222.cpp inline111.cpp
输出:
inline111: fun() = 222, &fun = 0x402980 inline222: fun() = 222, &fun = 0x402980
讨论:
-
本用例断言了在 Case A 中所讨论的内容。
-
请注意一个重要的点,即如果您在 inline222.cpp 中注释掉对
fun()
的实际调用(例如,在 inline222.cpp 中完全注释掉cout
语句),那么尽管翻译单元的编译顺序,fun()
将在其在 inline111.cpp 中首次遇到调用时被实例化,从而使得 Case B 的打印输出为inline111: fun() = 111, &fun = 0x402980
。
-
-
Case 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()
实际上已经内联扩展了(根据在该特定翻译单元中的定义),导致两个不同的fun()
打印输出。尽管如此,仍然只有一个全局链接的fun()
实例(正如标准所要求的那样),这从相同的&fun
打印输出中可以看出。
- 正如此处所述,
哦,天哪,这是我最讨厌的事情之一。
inline
更像是 static
或 extern
,而不是一个告诉编译器将你的函数内联的指令。 extern
、static
、inline
都是链接指令,几乎只用于链接器,而不是编译器。
据说 inline
是向编译器暗示你认为该函数应该被内联。这可能在1998年是正确的,但十年后编译器不需要这样的暗示。更不用说,当涉及到优化代码时,人类通常是错误的,所以大多数编译器会直接忽略这个“暗示”。
-
static
- 变量/函数名不能在其他翻译单元中使用。链接器需要确保它不会意外使用来自另一个翻译单元的静态定义变量/函数。 -
extern
- 在这个翻译单元中使用这个变量/函数名,但如果没有定义就不会出错。链接器会对试图使用某些 extern 符号的所有代码进行排序,确保它们的地址。 -
inline
- 这个函数将在多个翻译单元中定义,不用担心。链接器需要确保所有翻译单元都使用一个变量/函数的实例。
注意:通常,将模板声明为 inline
是没用的,因为它们已经具有 inline
的链接语义。但是,模板的显式特化和实例化需要使用 inline。
针对您的问题,提供以下具体答案:
-
在C++中,何时应该为函数/方法添加关键字'inline'?
只有在您希望该函数在头文件中被定义时才添加'inline'关键字。更确切地说,只有当该函数的定义可能会出现在多个翻译单元中时,才需要添加'inline'关键字。将一行代码定义为小规模的函数是个好主意,因为这将为编译器提供更多优化代码的信息。但同时这也会增加编译时间。
-
在C++中,何时不应该添加函数/方法的'inline'关键字?
不要添加inline关键字,只是因为认为如果编译器将其内联,代码会运行得更快。
-
编译器在何时无法知道何时将函数/方法作为'inline'处理?
通常情况下,编译器可以比您更好地完成此任务。但是,如果没有函数定义,编译器就无法选择内联代码。在最大程度优化的代码中,通常所有私有方法都将被内联,无论您是否要求。
另外,为了防止在GCC中内联,请使用
__attribute__(( noinline ))
,在Visual Studio中使用__declspec(noinline)
。 -
当函数/方法被赋予'inline'关键字时,应用程序是否为多线程对其有影响?
多线程不会以任何方式影响内联。