GCC 4.7.2编译此代码如下:
Obj::getNumber()中Obj::defaultNumber()是内联的Obj::getNumber()是单独编译和导出的,可以从不同的翻译单元链接.VC++ 2012在链接步骤失败:
Error 1 error LNK2001: unresolved external symbol "public: virtual int __thiscall Obj::getNumber(unsigned int)" 在 main.objVC++似乎在内部调用内联Obj::defaultNumber()但不在getNumber()符号表中导出.
VC++可以通过以下方式之一进行编译:
inline从getNumber()定义中删除关键字,或virtual从getNumber()声明中删除关键字(为什么!?)乍一看,GCC的行为似乎更有帮助/更直观.也许熟悉标准的人可能会在这里指出我正确的方向.
.
// Obj.h
class Obj
{
public:
virtual int getNumber(unsigned int i);
virtual int defaultNumber();
};
// Obj.cpp
static const int int_table[2] = { -1, 1 };
inline int Obj::getNumber(unsigned int i)
{
return int_table[i];
}
int Obj::defaultNumber()
{
return getNumber(0);
}
// main.cpp
#include <iostream>
#include "Obj.h"
int main()
{
Obj obj;
std::cout << "getNumber(1): " << obj.getNumber(1) << std::endl;
std::cout << "defaultNumber(): " << obj.defaultNumber() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
我完全改变了答案一次,为什么不两次?对于任何错别字都很晚了.
他们俩都这样做.这是未定义的行为.
因为您的代码符合非标准,编译器可以根据自己的判断来解释您的代码.
该标准规定,如果不是纯粹的,则每个定义规则使用一个虚拟成员,并且每个翻译单元都需要自己定义的odr使用的内联函数.
§3.2
2)"[...]如果虚拟成员函数不纯,则使用它是有效的.[...]"
3)"[...]内联函数应在每个翻译单元中定义. odr-used.[...]"§7.1.2
4)内联函数应在每个使用过的翻译单元中定义,并且在每种情况下都应具有完全相同的定义(3.2).[...]
此外,该标准要求在每个翻译单元内内联声明该功能,但允许编译器省略任何诊断.
§7.1.2
4)[......]如果在一个翻译单元中内联声明具有外部链接的功能,则应在其出现的所有翻译单元中内联声明; 无需诊断.[...]
既然你getNumber()没有在内部声明内联main.cpp,Obj.cpp那么你就处于未定义行为的土地上.
(编辑)我解释这个(参见下面的标准引用),不需要编译器检查函数是否在其出现的任何地方内联声明.我不知道但是怀疑这个"编译器不必检查"-rule没有引用句子"内联函数应该在每个使用它的翻译单元中定义[...] ".
MSVC编译即使getNumber在main中另外定义(也没有从main的角度声明内联),即使Obj.cpp实现仍然内联声明并存在于符号表中.即使代码不符合,标准也允许编译器的这种行为:未定义的行为(UB).(/编辑)
因此,两个编译器都可以自由地接受,编译或拒绝您的代码.
这里的问题是:为什么VC++没有实例化虚拟内联函数,但如果虚拟被遗漏了呢?
你可能会在SO周围阅读这些陈述,其中说"UB可能会炸毁你的房子,杀死你的妈妈,结束世界"等等.我不知道为什么没有函数实例,如果函数是虚拟的,否则就有了.我猜这就是UB的意思 - 奇怪的事情.
virtual)都提供内联函数的外部链接函数实例?编译器不需要实际替换内联函数,它们可能会产生函数的实例.由于内联函数默认具有外部链接,因此getNumber如果创建了实例,则可以从main 调用.
7.1.2
2)在通话点执行此内联替换不需要实现; 但是,即使省略了这种内联替换,仍应遵守7.1.2定义的内联函数的其他规则.
4)具有外部链接的内联函数在所有翻译单元中应具有相同的地址.
§9.3
3)命名空间范围内的类的成员函数具有外部链接.
MSDN内联,__ inline,__ forceinline
inline关键字告诉编译器首选内联扩展.但是,编译器可以创建函数的单独实例(实例化)并创建标准调用链接,而不是插入代码内联.可能发生这种情况的两种情况是:
- 递归函数.
- 通过转换单元中其他位置指针引用的函数.
这些原因可能会干扰内联,如同其他人一样,由编译器自行决定; 您不应该依赖内联说明符来导致函数内联.
所以可能有一个"其他"原因使编译器实例化它并创建一个main.obj使用的符号,但我不知道为什么这个原因会在函数消失时消失virtual.
编译器可以或可以不实例化内联函数,并且它可以或可以不接受在一个内联中并且在另一个翻译单元中非内联的相同功能.要确定您的行为,您需要这样做
两个编译器都是正确的.您正在调用未定义的行为,根据7.1.2第2段不需要诊断:
内联函数应在每个使用过的翻译单元中定义,并且在每种情况下都应具有完全相同的定义(3.2).......如果在一个翻译单元中内联声明具有外部链接的功能,则应在其出现的所有翻译单元中内联声明; 无需诊断.
海湾合作委员会可以自由地做它所做的事情并给你一个可能正常工作的程序.VC++在拒绝代码方面同样有效.
| 归档时间: |
|
| 查看次数: |
509 次 |
| 最近记录: |