我有一个有内联成员的类,但后来我决定从头中删除实现,所以我将函数的成员体移到cpp文件中.起初我刚刚在头文件中留下了内联签名(草率我),程序无法正确链接.然后我修好了标题,当然一切正常.
但是不是内联完全可选吗?
在代码中:
第一:
//Class.h
class MyClass
{
void inline foo()
{}
};
Run Code Online (Sandbox Code Playgroud)
接下来改为(不会链接):
//Class.h
class MyClass
{
void inline foo();
};
//Class.cpp
void MyClass::foo()
{}
Run Code Online (Sandbox Code Playgroud)
然后(将工作正常):
//Class.h
class MyClass
{
void foo();
};
//Class.cpp
void MyClass::foo()
{}
Run Code Online (Sandbox Code Playgroud)
我认为内联是可选的,想象我可能会因为我的邋iness警告,但没想到链接错误.在这种情况下编译器应该做的正确/标准的事情是什么,我是否应该根据标准得到我的错误?
Joh*_*itb 37
实际上,存在这样一个定义规则,即必须在其使用的每个翻译单元中定义内联函数.血腥细节如下.第一3.2/3:
每个程序应该只包含该程序中使用的每个非内联函数或对象的一个定义; 无需诊断.该定义可以在程序中明确显示,可以在标准或用户定义的库中找到,或者(在适当的时候)隐式定义(见12.1,12.4和12.8).内联函数应在每个使用它的翻译单元中定义.
当然还有7.1.2/4:
内联函数应在每个使用它的翻译单元中定义,并且在每种情况下都应具有完全相同的定义(3.2).[注意:在定义出现在翻译单元之前,可能会遇到对内联函数的调用.]如果在一个翻译单元中内联声明具有外部链接的功能,则应在其出现的所有翻译单元中内联声明; 无需诊断.具有外部链接的内联函数在所有翻译单元中应具有相同的地址.外部内联函数中的静态局部变量始终引用同一个对象.extern内联函数中的字符串文字是不同翻译单元中的相同对象.
但是,如果在类定义中定义函数,则将其隐式声明为inline函数.这将允许您在程序中多次包含包含该内联函数体的类定义.由于该函数具有external链接,因此它的任何定义都将引用相同的函数(或更多血腥 - 相同entity).
关于我的主张的血腥细节.第一3.5/5:
此外,如果类的名称具有外部链接,则成员函数,静态数据成员,类范围的类或枚举具有外部链接.
然后3.5/4:
具有命名空间范围的名称具有外部链接(如果它是[...]命名类的名称(第9节),或者在typedef声明中定义的未命名类,其中类具有用于链接目的的typedef名称.
这个"用于链接目的的名称"是这个有趣的事情:
typedef struct { [...] } the_name;
Run Code Online (Sandbox Code Playgroud)
从现在开始,您的程序中有多个同一实体的定义,ODR的另一个内容恰好限制了您.3.2/5跟着无聊的东西.
可以有多个类类型的定义(第9节),枚举类型(7.2),带有外部链接的内联函数(7.1.2)[...]在程序中,每个定义出现在不同的翻译单元中,并且定义满足以下要求.鉴于这样一个名为D的实体在多个翻译单元中定义,那么
- D的每个定义应由相同的令牌序列组成; 和
- 在D的每个定义中,根据3.4查找的相应名称,应指在D的定义中定义的实体,或者在重载决议(13.3)之后和部分模板专业化匹配之后应引用同一实体(14.8) .3)[...]
我现在切断了一些不重要的东西.以上是关于内联函数的两个重要记忆.如果您多次定义extern内联函数,但确实以不同方式定义它,或者如果您定义它并且其中使用的名称解析为不同的实体,那么您正在执行未定义的行为.
必须在使用它的每个TU中定义函数的规则易于记忆.并且它也是容易记住的.但那个名字解析怎么样呢?这里有一些例子.考虑一个静态函数assert_it:
static void assert_it() { [...] }
Run Code Online (Sandbox Code Playgroud)
现在,既然static将它内部链接,当你将它包含在多个翻译单元中时,每个定义将定义一个不同的实体.这意味着,你是不是允许使用assert_it来自那将会在节目中被多次定义的外部内联函数:因为什么情况是,内联函数将把所谓的一个实体assert_it在一个TU,但同样的另一实体在另一个TU中命名.你会发现这一切都是无聊的理论和编译器不会抱怨,但我发现这个例子特别显示了ODR和实体之间的关系.
接下来是再次回到你的特定问题.
以下是相同的事情:
struct A { void f() { } };
struct A { inline void f(); }; void A::f() { } // same TU!
Run Code Online (Sandbox Code Playgroud)
但是这个是不同的,因为函数是非内联的.您将违反ODR,因为f如果您不止一次包含标题,则有多个定义
struct A { void f(); }; void A::f() { } // evil!
Run Code Online (Sandbox Code Playgroud)
现在,如果你inline在类中加入声明f,但是然后省略在标题中定义它,那么你违反了3.2/3(并且7.1.2/4说了同样的东西,只是更详细),因为该函数没有在该翻译单元中定义!
请注意,在C(C99)中,内联与C++中的语义不同.如果你创建一个extern内联函数,你应该首先阅读一些好的论文(最好是标准的),因为那些在C中真的很棘手(基本上,任何使用的函数的内联定义都需要另一个非内联函数定义) TU .C中的静态内联函数很容易处理.它们的行为与任何其他函数一样,除了具有通常的"内联替换"提示之外inline.C和C++中的静态仅用作内联替换提示.由于静态已经创建一个不同的实体,只要它被使用(因为内部链接),inline只会添加内联替换提示 - 而不是更多.
Jar*_*Par 10
该方法是否实际内联是由编译器自行决定的.但是,内联关键字的存在也会影响方法的链接.
C++链接不是我的专长所以我会推迟链接以获得更好的解释.
或者你可以等待litb在一小时左右提供血腥细节;)