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

9

由于编译器决定是否内联我的函数,因此我可以完全停止使用这个关键字吗(假设它将尽可能内联所有内容)?

2个回答

12

你可以停止使用 inline 作为一种优化技术。

inline 的作用基本上只有在你不想应用 ODR (One Definition Rule) 时才有用。简而言之,你可以将函数标记为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)。如果一个内联函数在 odr-used 中,则必须在使用它的每个翻译单元中定义它。

请注意,您可以将相同的函数标记为 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();
}

如果您在常用的#include头文件中直接提供函数定义,通常会将其标记为inline。这就是为什么要这样做的原因。

此外,请注意,类成员函数如果其定义直接内联在类定义中,则会自动标记为inline


即使您从未使用过 inline,您仍然可以隐式地使用它:在一个 .cpp 文件中编写 struct Foo{ int x; Foo():x(0) {} };,在另一个 .cpp 文件中编写 struct Foo{ double d; Foo():d(0) {} };,这样您就隐式地创建了两个名为 Foo::Fooinline 构造函数... - Yakk - Adam Nevraumont
1
@Yakk:除非两个“Foo”都在匿名命名空间中声明,否则您的程序将具有未定义的行为。 - Matthieu M.
@MatthieuM。是的,我在生产代码中也见过这种情况发生。基本上,我想说的是,即使您不使用“inline”,您也可能会受到其影响,因此即使您不使用它,您也需要了解“inline”的真正含义。 - Yakk - Adam Nevraumont

5

根据使用inline的目的而定。

常见的(误解)看法:
inline只是一个建议,编译器可以或不可以遵守。好的编译器总会做需要做的事情。

然而,真相是:

inline通常表示实现应该优先选择在调用点内联替换函数体,而不是使用普通的函数调用机制。实现不一定要在调用点执行此内联替换;但是,即使省略此inline内联替换,也会遵循其他规则(特别是与一义性规则相关的规则)。

因此,如果您使用的目的是优化,那么答案是:

是的,您可以停止使用inline。现代大多数编译器将为您优化得非常好

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

否,您需要明确标记函数为inline,以便能够绕过ODR。

注意:在类主体内定义的成员函数隐式地是inline,但对于自由函数不适用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接