4 c++
因此,我们将类定义放在标题中,并且由于 ODR(可能还有更多原因),我们不包括翻译单元而不是标题。此外,由于 ODR,您不应该在标题中添加变量和函数等的定义,因为它可能会违反规则......
但是由于ODR 也适用于类类型(对我来说是类定义),为什么可以不小心将其添加到标题中?
您对 ODR 和 ODR 使用感到困惑。
类型的 ODR 意味着一个类不能在一个翻译单元中定义多次。
如果翻译单元中有以下内容,编译器应该报告错误,因为foo违反了类型的 ODR。
struct foo {};
struct foo {};
void bar() {}
Run Code Online (Sandbox Code Playgroud)
ODR 使用不适用于类型。它适用于非内联函数和变量。
您帖子的链接中描述了 ODR 和 ODR 使用。
关于 ODR 使用的 SO 帖子:“ODR 使用”某物是什么意思?。
标准的“3.2 Ondefinition 规则”部分中有几条陈述,涵盖了规则的不同方面。
- 任何翻译单元不得包含对任何变量、函数、类类型、枚举类型或模板的多个定义。
这个涵盖了您问题中的类类型定义,但这仅适用于一个翻译单元(或源文件)。不同文件(翻译单元)中包含的标题中的相同类类型定义不违反这一点,因为它涉及多个翻译单元(并且在任何一个翻译单元中仍然只有一个定义)。
- 每个程序都应包含该程序中 odr 使用的每个非内联函数或变量的一个定义;无需诊断。定义可以显式地出现在程序中,可以在标准或用户定义的库中找到,或者(在适当的时候)它是隐式定义的(参见 12.1、12.4 和 12.8)。内联函数应在每个使用 odr 的翻译单元中定义。
此规则涵盖程序级别(可能包含多个翻译单元),但它仅影响odr 使用的非内联函数或可行的。包含在不同翻译单元中的类类型定义不包括在内,也不违反此规则。
并且,有一条规则专门处理出现在不同翻译单元中的类类型的定义:
类类型(第 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),但如果对象在 D 的所有定义中具有相同的字面量类型,并且对象用常量表达式 (5.19) 初始化,并且值 (但不使用对象的地址),并且该对象在 D 的所有定义中具有相同的值;和
- 在 D 的每个定义中,对应的实体应具有相同的语言链接;和
- 在 D 的每个定义中,所引用的重载运算符、对转换函数、构造函数、运算符 new 函数和运算符删除函数的隐式调用应引用相同的函数,或在 D 的定义中定义的函数;和
- 在 D 的每个定义中,(隐式或显式)函数调用使用的默认参数被视为其标记序列存在于 D 的定义中;也就是说,默认参数受上述三个要求的约束(并且,如果默认参数具有带有默认参数的子表达式,则此要求递归适用)。
- 如果 D 是一个具有隐式声明构造函数的类 (12.1),就好像构造函数是在每个使用 odr 的翻译单元中隐式定义的,并且每个翻译单元中的隐式定义应为 a 调用相同的构造函数基类或 D 的类成员。
简而言之,您可以在不同的翻译单元中多次定义类类型,但每个翻译单元最多只能有一个定义,并且所有定义必须相同。