C++ - 如果我们应该在 header 中定义方法,那么 inline 关键字有什么用?
C++ - What is the use of inline keyword if we should define method in header?
根据答案here,只需要在header文件中定义方法以使其内联。所以我的问题是,为什么有 inline
关键字?
简答。这不是必需的。编译器通常会忽略 inline
关键字。
其他问题中已经给出了更全面的答案,更不用说您链接的问题中的第二个答案了。
例如,如果要在header中定义一个自由函数(不是class的成员函数),则需要声明它inline
,否则将其包含在多个翻译单元中会导致 ODR 违规。
通常的做法是在header中声明函数,在单独编译的.cpp
文件中定义。但是,在 header 中将其定义为 inline
函数允许包含它的每个翻译单元都有其可用的定义,这使得该函数更容易实际内联;如果函数被大量使用,这很重要。
inline
关键字的历史原因是旧的 C 编译器无法像现代 C 和 C++ 编译器那样优化代码。因此,最初引入它是为了允许程序员指示对 inline
函数的偏好(有效地将函数 body 直接插入调用者,这避免了与函数调用相关的开销)。由于无法保证编译器 可以 内联函数(例如,某些编译器只能内联某些类型的函数),因此 inline
关键字被用作提示而不是指令。
inline
的其他典型用法不是自由裁量的——如果一个函数在多个编译单元中定义(例如,因为包含 header 的 #include
d 不止一次)然后离开 inline
会导致违反一个定义规则(因此会导致未定义的行为)。 inline
是给编译器的一条指令(编译器可能会向链接器发出指令)以解决这种偶然违反 one-definition 规则的情况,并生成一个工作程序(例如,不会导致链接器抱怨多个定义)。
至于为什么仍然需要它,从功能上讲,预处理器只进行文本替换......例如用包含的 header.
的内容替换 #include
指令
让我们考虑两种情况。第一个有两个源文件(编译单元);
#include "some_header"
//some other functions needed by the program, not pertinent here
和
#include "some_header"
int main()
{
foo();
}
其中 some_header
包含定义
void foo() { /* whatever */ }
然而,第二种情况省略了 header 文件(是的,人们这样做了)并将第一个源文件作为
void foo() { /* whatever */ }
//some other functions needed by the program, not pertinent here
第二个为;
void foo() { /* whatever */ }
int main()
{
foo();
}
在这两种情况下,假设两个编译单元被编译并链接在一起。结果违反了一个定义规则(实际上,由于多重定义的符号,通常会导致链接器错误)。
根据当前的语言规则(具体来说,预处理器只进行文本替换),这两种情况需要在功能上完全等同。如果一种情况是打印值 42
,那么另一种情况也应该如此。如果一个有未定义的行为(就像这里的情况一样),另一个也是如此。
但是,为了便于讨论,如果一个函数定义在 header 中,则需要神奇地内联该函数。这两种情况下的代码将不再等效。 header 文件版本将具有已定义的行为(不违反一个定义规则)而第二个将具有未定义的行为。
糟糕。我们刚刚打破了这两种情况的等价性。这可能看起来不多,但程序员实际上很难理解为什么一个版本链接而另一个版本不链接。他们将没有办法解决这个问题......除了将代码移动到 header 文件中。
这意味着,我们需要一些方法使这两个场景等效。这意味着代码中需要有一些东西使它们等价。输入 inline
关键字,并将其添加到 foo()
.
的定义前
现在,好吧,有人可能会争辩说预处理器应该做一些更智能的事情,即做的不仅仅是简单的文本替换。但是,现在你正处于湿滑的斜坡上。 C 和 C++ 标准确实(详细地)指定预处理器执行此操作。改变这一点会带来一连串的其他变化。不管这是个好主意(当然,有人提倡从 C++ 中完全消除预处理器),这是一个更大的变化,对语言和程序员(谁,它是否好或者不好,可以依赖于预处理器的行为)。
根据答案here,只需要在header文件中定义方法以使其内联。所以我的问题是,为什么有 inline
关键字?
简答。这不是必需的。编译器通常会忽略 inline
关键字。
其他问题中已经给出了更全面的答案,更不用说您链接的问题中的第二个答案了。
例如,如果要在header中定义一个自由函数(不是class的成员函数),则需要声明它inline
,否则将其包含在多个翻译单元中会导致 ODR 违规。
通常的做法是在header中声明函数,在单独编译的.cpp
文件中定义。但是,在 header 中将其定义为 inline
函数允许包含它的每个翻译单元都有其可用的定义,这使得该函数更容易实际内联;如果函数被大量使用,这很重要。
inline
关键字的历史原因是旧的 C 编译器无法像现代 C 和 C++ 编译器那样优化代码。因此,最初引入它是为了允许程序员指示对 inline
函数的偏好(有效地将函数 body 直接插入调用者,这避免了与函数调用相关的开销)。由于无法保证编译器 可以 内联函数(例如,某些编译器只能内联某些类型的函数),因此 inline
关键字被用作提示而不是指令。
inline
的其他典型用法不是自由裁量的——如果一个函数在多个编译单元中定义(例如,因为包含 header 的 #include
d 不止一次)然后离开 inline
会导致违反一个定义规则(因此会导致未定义的行为)。 inline
是给编译器的一条指令(编译器可能会向链接器发出指令)以解决这种偶然违反 one-definition 规则的情况,并生成一个工作程序(例如,不会导致链接器抱怨多个定义)。
至于为什么仍然需要它,从功能上讲,预处理器只进行文本替换......例如用包含的 header.
的内容替换#include
指令
让我们考虑两种情况。第一个有两个源文件(编译单元);
#include "some_header"
//some other functions needed by the program, not pertinent here
和
#include "some_header"
int main()
{
foo();
}
其中 some_header
包含定义
void foo() { /* whatever */ }
然而,第二种情况省略了 header 文件(是的,人们这样做了)并将第一个源文件作为
void foo() { /* whatever */ }
//some other functions needed by the program, not pertinent here
第二个为;
void foo() { /* whatever */ }
int main()
{
foo();
}
在这两种情况下,假设两个编译单元被编译并链接在一起。结果违反了一个定义规则(实际上,由于多重定义的符号,通常会导致链接器错误)。
根据当前的语言规则(具体来说,预处理器只进行文本替换),这两种情况需要在功能上完全等同。如果一种情况是打印值 42
,那么另一种情况也应该如此。如果一个有未定义的行为(就像这里的情况一样),另一个也是如此。
但是,为了便于讨论,如果一个函数定义在 header 中,则需要神奇地内联该函数。这两种情况下的代码将不再等效。 header 文件版本将具有已定义的行为(不违反一个定义规则)而第二个将具有未定义的行为。
糟糕。我们刚刚打破了这两种情况的等价性。这可能看起来不多,但程序员实际上很难理解为什么一个版本链接而另一个版本不链接。他们将没有办法解决这个问题......除了将代码移动到 header 文件中。
这意味着,我们需要一些方法使这两个场景等效。这意味着代码中需要有一些东西使它们等价。输入 inline
关键字,并将其添加到 foo()
.
现在,好吧,有人可能会争辩说预处理器应该做一些更智能的事情,即做的不仅仅是简单的文本替换。但是,现在你正处于湿滑的斜坡上。 C 和 C++ 标准确实(详细地)指定预处理器执行此操作。改变这一点会带来一连串的其他变化。不管这是个好主意(当然,有人提倡从 C++ 中完全消除预处理器),这是一个更大的变化,对语言和程序员(谁,它是否好或者不好,可以依赖于预处理器的行为)。