如果内联是可选的,为什么删除“inline”会导致错误? 导致链接器错误?
我有一个具有内联成员的类,但后来我决定要从标头中删除实现,因此我将函数的成员主体移到了 cpp 文件中。 起初我只是在头文件中留下了内联签名(我太草率了),程序无法正确链接。 然后我修复了我的标题,当然一切都正常。
但是 inline 不是完全可选的吗?
在代码中:
First:
//Class.h
class MyClass
{
void inline foo()
{}
};
Next 更改为 (不会链接):
//Class.h
class MyClass
{
void inline foo();
};
//Class.cpp
void MyClass::foo()
{}
然后更改为 (会正常工作):
//Class.h
class MyClass
{
void foo();
};
//Class.cpp
void MyClass::foo()
{}
我认为 inline 是可选的,并想象我可能会得到对我的草率提出警告,但没想到会出现链接错误。 在这种情况下,编译器应该做的正确/标准的事情是什么?根据标准,我应该承担我的错误吗?
I have a class that had an inline member, but I later decided that I wanted to remove the implementation from the headers so I moved the members body of the functions out to a cpp file. At first I just left the inlined signature in the header file (sloppy me) and the program failed to link correctly. Then I fixed my header and it all works fine, of course.
But wasn't inline totally optional?
In code:
First:
//Class.h
class MyClass
{
void inline foo()
{}
};
Next changed to (won't link):
//Class.h
class MyClass
{
void inline foo();
};
//Class.cpp
void MyClass::foo()
{}
And then to (will work fine):
//Class.h
class MyClass
{
void foo();
};
//Class.cpp
void MyClass::foo()
{}
I thought inline was optional, and imagined I might get by with a warning for my sloppiness, but didn't expect a linking error. What's the correct/standard thing a compiler should do in this case, did I deserve my error according to the standard?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
事实上,有这样一个定义规则:内联函数必须在它使用的每个翻译单元中定义。 血淋淋的细节如下。 第一个
3.2/3
:当然还有
7.1.2/4
:但是,如果您在类定义中定义函数,则它会隐式声明为
inline
函数。 这将允许您在程序中多次包含包含该内联函数体的类定义。 由于该函数具有外部
链接,因此它的任何定义都将引用相同函数(或更血淋淋的 - 相同实体
)。关于我的主张的血淋淋的细节。 第一个
3.5/5
:然后
3.5/4
:这个“用于链接目的的名称”是一件有趣的事情:
由于现在您的程序中对同一实体有多个定义,ODR 的另一件事恰好限制了您。
3.2/5
接下来是无聊的东西。我现在剪掉了一些不重要的东西。 以上是关于内联函数需要记住的两个重要内容。 如果您多次定义外部内联函数,但以不同的方式定义它,或者如果您定义它并且其中使用的名称解析为不同的实体,那么您将执行未定义的行为。
必须在使用该函数的每个 TU 中定义该函数的规则很容易记住。 而且它是相同的也很容易记住。 但是名称解析呢? 这里有一些例子。 考虑一个静态函数
assert_it
:现在,由于
static
会给它内部链接,当你将它包含到多个翻译单元中时,每个定义都会定义一个不同的实体。 这意味着您不允许从将在程序中多次定义的外部内联函数中使用assert_it
:因为发生的情况是内联函数将引用一个 TU 中名为assert_it
的实体,但另一个 TU 中另一个同名的实体。 您会发现这一切都是无聊的理论,编译器可能不会抱怨,但我发现这个示例特别显示了 ODR 和实体之间的关系。接下来再次回到您的特定问题。
以下是相同的内容:
但这一个不同,因为该函数是非内联的。 您将违反 ODR,因为如果多次包含标头,您就有多个
f
定义现在,如果您将
inline
放在f 的声明上
在类中,但随后省略在标头中定义它,那么您就违反了3.2/3
(和7.1.2/4
说的是同样的事情,只是更详细),因为该函数未在该翻译单元中定义!请注意,在 C (C99) 中,内联具有与 C++ 中不同的语义。 如果你创建一个外部内联函数,你应该首先阅读一些好的论文(最好是标准),因为这些在 C 中确实很棘手(基本上,任何使用的函数的内联定义都需要另一个非内联函数定义在另一个TU. C 中的静态内联函数很容易处理,除了 C 和 C++ 中通常的“内联替换”提示仅用作内联之外。由于 static 在任何时候使用时都会创建一个不同的实体(由于内部链接),因此
inline
将仅添加内联替换提示 - 而不是更多。Indeed, there is this one definition rule saying that an inline function must be defined in every translation unit it is used. Gory details follow. First
3.2/3
:And of course
7.1.2/4
:However, if you define your function within the class definition, it is implicitly declared as
inline
function. That will allow you to include the class definition containing that inline function body multiple times in your program. Since the function hasexternal
linkage, any definition of it will refer to the same function (or more gory - to the sameentity
).Gory details about my claim. First
3.5/5
:Then
3.5/4
:This "name for linkage purposes" is this fun thing:
Since now you have multiple definitions of the same entity in your programs, another thing of the ODR happens to restrict you.
3.2/5
follows with boring stuff.I cut off some unimportant stuff now. The above are the two important one to remember about inline functions. If you define an extern inline function multiple times, but do define it differently, or if you define it and names used within it resolve to different entities, then you are doing undefined behavior.
The rule that the function has to be defined in every TU in which it is used is easy to remember. And that it is the same is also easy to remember. But what about that name resolution thingy? Here some example. Consider a static function
assert_it
:Now, since
static
will give it internal linkage, when you include it into multiple translation units, then each definition will define a different entity. This means that you are not allowed to useassert_it
from an extern inline function that's going to be defined multiple times in the program: Because what happens is that the inline function will refer to one entity calledassert_it
in one TU, but to another entity of the same name in another TU. You will find that this all is boring theory and compilers won't probably complain, but i found this example in particular shows the relation between the ODR and entities.What follows is getting back to your particular problem again.
Following are the same things:
But this one is different, since the function is non-inline. You will violate the ODR, since you have more than one definition of
f
if you include the header more than onceNow if you put
inline
on the declaration off
inside the class, but then omit defining it in the header, then you violate3.2/3
(and7.1.2/4
which says the same thing, just more elaborating), since the function isn't defined in that translation unit!Note that in C (C99), inline has different semantics than in C++. If you create an extern inline function, you should first read some good paper (preferably the Standard), since those are really tricky in C (basically, any used inline-definition of a function will need another, non-inline function definition in another TU. static inline functions in C are easy to handle. They behave like any other function, apart of having the usual "inline substitution" hint. static
inline
in both C and C++ serve only as a inline-substitution hint. Since static will already create a different entity any time it's used (because of internal linkage),inline
will just add the inline-substitution hint - not more.该方法是否实际内联由编译器自行决定。 然而inline关键字的存在也会影响方法的链接。
C++ 链接不是我的专长,因此我将遵循链接以获得更好的解释。
或者,您也可以等待litb 在一个小时左右提供血淋淋的细节;)
Whether or not the method is actually inlined is at the sole discretion of the compiler. However the presence of the inline keyword will also affect the linkage of the method.
C++ linkage is not my specialty so I'll defer to the links for a better explanation.
Alternately you can just wait for litb to provide the gory details in an hour or so ;)
需要注意的是:当方法被声明为内联时,它的定义必须与其声明在一起。
Point to note: when method is declared inline, its definition MUST be together with its declaration.
关于harrath.jr的答案,如果方法的定义具有“inline”关键字,并且该定义在同一标头中可用,则不需要声明内联方法,ie:
这对于有条件内联方法很有用取决于构建是“debug”还是“release”,如下所示:
“内联”文件可能如下所示:
并且实现可能如下所示
:不太漂亮,但当激进的内联成为调试难题时,它有它的用处。
Regarding harshath.jr's answer, a method need not be declared inline if its definition has the "inline" keyword, and that definition is available in the same header, i.e.:
This is useful for conditionally inlining a method depending on whether or not the build is "debug" or "release" like so:
The "inline" file could look like:
and the implementation could like the following:
It's not exactly pretty but it has it's uses when aggressive inline becomes a debugging headache.