考虑以下示例:
// usedclass1.hpp
#include <iostream>
class UsedClass
{
public:
UsedClass() { }
void doit() { std::cout << "UsedClass 1 (" << this << ") doit hit" << std::endl; }
};
// usedclass2.hpp
#include <iostream>
class UsedClass
{
public:
UsedClass() { }
void doit() { std::cout << "UsedClass 2 (" << this << ") doit hit" << std::endl; }
};
// object.hpp
class Object
{
public:
Object();
};
// object.cpp
#include "object.hpp"
#include "usedclass2.hpp"
Object::Object()
{
UsedClass b;
b.doit();
}
// main.cpp
#include "usedclass1.hpp"
#include "object.hpp"
int main()
{
Object obj;
UsedClass a;
a.doit();
}
代码编译后没有任何编译器或链接器错误。但输出对我来说很奇怪:
-
Fedora x86_64 上的 gcc (Red Hat 4.6.1-9) 没有优化 [EG1]:
使用类 1 (0x7fff0be4a6ff) doit hit
使用类 1 (0x7fff0be4a72e) doit hit
-
与 [EG1] 相同,但启用了 -O2 选项 [EG2]:
使用类 2 (0x7fffcef79fcf) doit hit
使用 1 类 (0x7fff f79f) 击中
-
Windows XP 32 位上的 msvc2005 (14.00.50727.762),无优化 [EG3]:
已使用Class 1 (0012FF5B) doit hit
已使用Class 1 (0012FF67) doit hit
-
与 [EG3] 相同,但启用了 /O2(或 /Ox)[EG4]:
已使用Class 1 (0012FF73) doit hit
已使用Class 1 (0012FF7F) doit hit
我期望出现链接器错误(假设违反 ODR 规则)或 [EG2] 中的输出(代码内联,没有从翻译单元导出任何内容,保留 ODR 规则)。因此我的问题是:
- 为什么可以输出 [EG1]、[EG3]、[EG4]?
- 为什么不同的编译器甚至同一个编译器会得到不同的结果?这让我认为标准在某种程度上没有指定这种情况下的行为。
感谢您的任何建议、评论和标准解释。
Update
我想了解编译器的行为。更准确地说,为什么违反 ODR 时不会生成错误。一个假设是,由于类中的所有函数二手Class1 and 二手Class2被标记为内联(因此 C++03 3.2 是not违反)链接器不报告错误,但在这种情况下输出 [EG1]、[EG3]、[EG4] 看起来很奇怪。
这是标准第 3.2 节中禁止您所做的事情的规则(C++11 措辞):
一个类类型可以有多个定义(第 9 条),枚举类型(7.2),具有外部链接的内联函数(7.1.2)、类模板(第 14 条)、非静态函数模板(14.5.6)、类模板的静态数据成员(14.5.1.3)、类模板的成员函数(14.5.1.1),或未指定某些模板参数的模板特化(14.7、14.5.5)在程序中,前提是每个定义出现在不同的翻译单元中,并且定义满足以下要求。给定这样一个名为D
在多个翻译单元中定义,然后
-
每个定义D
应由相同的令牌序列组成; and
-
在每个定义中D
,根据 3.4 查找的相应名称应指代定义中定义的实体D
,或在重载解析(13.3)之后和部分模板特化(14.8.3)匹配之后应引用相同的实体,除了名称可以引用const
如果对象在所有定义中具有相同的文字类型,则具有内部链接或无链接的对象D
,并且用常量表达式(5.19)初始化该对象,并使用该对象的值(但不是地址),并且该对象在所有定义中具有相同的值D
; and
-
在每个定义中D
,对应实体应具有相同的语言链接;和
-
在每个定义中D
、所指的重载运算符、对转换函数、构造函数、运算符 new 函数和运算符删除函数的隐式调用应引用同一函数,或引用 D 定义中定义的函数;和
-
在每个定义中D
,(隐式或显式)函数调用使用的默认参数被视为其标记序列存在于定义中D
;也就是说,默认参数须满足上述三个要求(并且,如果默认参数具有带默认参数的子表达式,则此要求递归适用)。
-
if D
是一个具有隐式声明构造函数的类(12.1),就好像该构造函数在每个使用 odr 的翻译单元中隐式定义,并且每个翻译单元中的隐式定义应为基类调用相同的构造函数或以下类别的成员D
.
在您的程序中,您违反了 ODRclass UsedClass
因为不同编译单元的token是不同的。您可以通过移动定义来解决这个问题UsedClass::doit()
在类主体之外,但相同的规则适用于内联函数的主体。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)