我可以完全停止使用"inline"吗?

Can I stop using "inline" altogether?

本文关键字:inline 我可以      更新时间:2023-10-16

因为它完全取决于编译器是否内联我的函数,我可以完全停止使用这个关键字(假设它将内联一切,无论如何)?

您可以停止使用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();
}

main.cpp

#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段,实际上:

每个程序应该只包含一个非内联函数或变量的定义在那个项目中;不需要诊断。定义可以显式地出现在程序中,可以找到它在标准库或用户定义库中,或者(适当时)隐式定义(参见12.1,12.4和12.8)。内联函数应在每一个使用该函数的翻译单元中定义。

注意,您可以将相同的函数标记为inline,并在不同的翻译单位中字面上定义两次,只要这些定义相同就可以:

foo.cpp

inline int foo() // Has the same body in main.cpp, no problem!
{
    return 42;
}

main.cpp

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
}

main.cpp

inline int foo()
{
    return -42; // <== WATCH OUT! Different body in foo.cpp
}
int main()
{
    std::cout << foo();
}

这就是为什么当你在通常的 #included头文件中直接提供函数定义时,通常将函数标记为inline

还要注意,定义直接内联在类定义中的类成员函数被自动标记为inline

取决于使用inline的目的。

常见的(错误的)概念:
inline只是一个编译器可能遵守也可能不遵守的建议。一个好的编译器无论如何都会做需要做的事情。

然而,真相:

inline通常向实现表明,在调用点对函数体进行内联替换比通常的函数调用机制更可取。实现不需要在调用点执行此内联替换;然而,即使省略了inline的替换,也遵循inline的其他规则(特别是w.r.t One Definition Rule)。

所以如果你使用的目的是优化答案是:

YES,您可以停止使用inline。大多数现代编译器都可以很好地为您完成

但是,如果您使用inline的目的是允许您通过一个定义规则并在头文件中定义函数体而不破坏ODR,那么答案是:

NO,您需要显式地将函数标记为inline,以便能够绕过ODR。

注意:在类体中定义的成员函数是隐式的inline,但这并不适用于自由函数。