因为它是否内联我的函数完全取决于编译器,所以我可以完全停止使用这个关键字(假设它无论如何都会内联所有内容)?
2 回答
您可以停止使用inline
作为优化技术。
inline
基本上只有当您不希望ODR(一个定义规则)适用时才有用。简而言之,您可以将函数标记为inline
并直接在一个头文件中提供它们的定义,然后由多个翻译单元导入,而链接器不会抱怨这一点:
foo.hpp
inline int foo() { return 42; }
foo.cpp
#include "foo.hpp" // <== THE LINKER WOULD COMPLAIN IF foo() WERE NOT inline,
// because its definition is imported also in main.cpp
void bar()
{
int x = foo();
}
主文件
#include "foo.hpp" // <== THE LINKER WOULD COMPLAIN IF foo() WERE NOT inline,
// because its definition is imported also in foo.cpp
int main()
{
std::cout << foo();
}
关键字的存在inline
(同样不能保证编译器将执行内联)确保链接器将合并这些定义。
当然,为了使这个合并操作有意义,标记为最终成为几个不同翻译单元的一部分的函数的所有定义必须相同。如果不是这种情况,则您的程序具有未定义的行为并且不需要诊断 - 这意味着您的编译器/链接器不需要检测这种不一致并告诉您有问题!inline
实际上,根据 C++11 标准的第 3.2/4 段:
每个程序都应包含该程序中 odr 使用的每个非内联函数或变量的准确定义;无需诊断。定义可以显式出现在程序中,可以在标准或用户定义库中找到,或者(在适当时)隐式定义(参见 12.1、12.4 和 12.8)。内联函数应在使用它的每个翻译单元中定义。
请注意,您可以在不同的翻译单元中将相同的函数标记为inline
并按字面意思定义两次,只要这些定义相同就可以:
foo.cpp
inline int foo() // Has the same body in main.cpp, no problem!
{
return 42;
}
主文件
inline int foo() // Has the same body in foo.cpp, no problem!
{
return 42;
}
int main()
{
std::cout << foo();
}
但是,如果这两个定义不同,您将在代码中注入 UB,如下例所示:
foo.cpp
inline int foo()
{
return 42; // <== WATCH OUT! Different body in main.cpp
}
主文件
inline int foo()
{
return -42; // <== WATCH OUT! Different body in foo.cpp
}
int main()
{
std::cout << foo();
}
这就是为什么您通常将函数标记为inline
直接在通用#include
头文件中提供定义的原因。
另请注意,其定义直接内联在类定义中的类成员函数会自动标记为inline
.
取决于使用目的inline
。
常见的(错误)概念:
inline
只是编译器可能会或可能不会遵守的建议。一个好的编译器无论如何都会做需要做的事情。
同时,真相:
inline
通常向实现表明,在调用点对函数体进行内联替换优于通常的函数调用机制。在调用点执行此内联替换不需要实现;但是,即使inline
省略了此替换,也会遵循其他规则(尤其是One Definition Rule)inline
。
因此,如果您的使用目的是优化,答案是:
是的,您可以停止使用
inline
. 大多数现代编译器都会很好地为您做到这一点。
但是,如果您使用的目的inline
是让您超越一个定义规则并在头文件中定义一个函数体而不破坏 ODR,那么答案是:
否,您需要明确标记这些功能
inline
才能绕过 ODR。
注意:类体内定义的成员函数是隐式的inline
,但同样不适用于自由函数。