因此我们将类定义放在标题中,由于ODR(我们可能有更多原因),我们不包括翻译单元而不是标题。另外,由于ODR,你不应该在标题中添加变量和函数等的定义,因为它可能会破坏规则....
但是由于ODR also applies for class types(对我来说是类定义),为什么可以不加谨慎地添加到标题中?
答案 0 :(得分:3)
您在使用ODR和ODR之间感到困惑。
类型的ODR意味着类在翻译单元中不能定义多次。
如果您在翻译单元中有以下内容,则编译器应报告错误,因为违反了foo
类型的ODR。
struct foo {};
struct foo {};
void bar() {}
ODR使用不适用于类型。它适用于非内联函数和变量。
ODR和ODR使用都在the link from your post中描述。
关于使用ODR的SO帖子:What does it mean to "ODR-use" something?。
答案 1 :(得分:2)
标准的“3.2 Ondefinition rule”部分有几个陈述,涵盖规则的不同方面。
- 任何翻译单元都不得包含任何变量,函数,类类型,枚举类型或模板的多个定义。
醇>
此问题涵盖了您问题中的班级类型定义,但这仅适用于一个翻译单元(或源文件)。包含在不同文件(翻译单元)中的标题中的相同类类型定义不违反此规则,因为它涉及多个翻译单元(并且在任何一个翻译单元中仍然只有一个定义)。
- 每个程序应该只包含该程序中使用的每个非内联函数或变量的一个定义;无需诊断。定义可以在程序中明确显示,可以找到 在标准或用户定义的库中,或(在适当时)它是隐式定义的(见12.1,12.4和12.8)。内联函数应在每个使用它的翻译单元中定义。
醇>
此规则涵盖程序级别(可能包含多个翻译单元),但它仅影响使用率较高的非内联函数或可行。不包含在不同翻译单元中的类类型定义,并且不违反此规则。
并且,有一条规则专门处理出现在不同翻译单元中的类类型的定义:
- 醇>
类类型(第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的每个定义应由相同的令牌序列组成;和
- 在D的每个定义中,相应的名称,根据3.4查找,应指在D的定义内定义的实体,或者在重载解析(13.3)和匹配部分模板后应引用同一实体特化(14.8.3),但名称可以引用const对象 如果对象在D的所有定义中具有相同的文字类型,并且使用常量表达式(5.19)初始化对象,并且使用对象的值(但不是地址),则使用内部链接或无链接在D的所有定义中具有相同的值;和
- 在D的每个定义中,相应的实体应具有相同的语言链接;和
- 在D的每个定义中,所引用的重载运算符,对转换函数,构造函数,运算符新函数和运算符删除函数的隐式调用,应引用相同的函数,或者引用D定义中定义的函数;和
- 在D的每个定义中,(隐式或显式)函数调用使用的默认参数被视为其标记序列存在于D的定义中;也就是说,默认参数受上述三个要求的约束(并且,如果默认参数具有带有默认参数的子表达式,则此要求将递归应用)。
- 如果D是具有隐式声明的构造函数(12.1)的类,就好像构造函数在每个使用它的翻译单元中被隐式定义,并且每个翻译单元中的隐式定义应该调用相同的基类的构造函数或D.的类成员。
简而言之,您可以在不同的翻译单元中多次定义类类型,但每个翻译单元最多只能有一个定义,并且所有定义必须相同。