C/C++编程:单一定义规则ODR(不理解)

ODR规则

  • 任何变量、函数、类类型、枚举类型、概念 (C++20 起)或模板,在每个翻译单元中都只允许有一个定义(其中部分可以有多个声明,但是只允许有一个定义)
  • 在整个程序中,被ODR式使用inline函数或变量只允许有且仅有一个定义。。编译器不要求对这条规则的违反进行诊断,但违法它的行为是未定义的
  • 对于inline函数inline 变量 (C++17 起)来说,在ODR式使用了它的每个翻译单元中都需要一个定义
  • 在以需要将类作为完整类型的方式给予的每个翻译单元中,要求有且仅有该类的一个定义。

以下各种实体:类类型、枚举类型、inline函数、inline变量 (C++17 起)、模板实体(模板和模板成员,但不完全模板特化),在程序中可以出现多个定义,只要满足下列条件

  • 每个定义出现在不同翻译单元
  • 定义不附着到具名模块(C++20 起)
  • 每个定义都由相同的记号序列构成(典型情况下都是在同一个头文件中)
  • 每个定义内进行名字查找(在重载决议后)都找到相同实体,除了
    • 具有内部连接或无连接的常量可以指代不同对象,只要不ORD使用它们的基类序列被唯一标志(C++11起)
    • 不在默认实参或默认模板实参 (C++20 起)中的 lambda 表达式由定义它们的记号序列被唯一标识
      (C++11 起)
  • 被重载的运算符(包括转换、分配和解分配函数),在各个定义中都代表相同的函数(除非它们代表的是在这个定义中所定义的函数)
  • 它们具有相同的语言连接(比如包含文件时并未处于某个 extern “C” 块之中)
  • 以上三条规则同样适用于各个定义中的所有默认实参
  • 如果该定义是模板定义、则所有这些要求一同适用于定义点大的各个名字和实例化的各个待决名

若满足了所有这些要求,则程序的行为如同在整个程序中只有一个定义。否则程序非良构,不要求诊断。

注意:

  • 在C中,类型没有全程序范围的ODR、而同一变量的extern声明甚至可以在不同的翻译单元中具有不同的类型,只要它们是兼容的类型即可。
  • 在 C++ 中,用于同一个类型的声明的源代码记号必须与上述相同:如果一个 .cpp 文件定义了 struct S { int x; }; 而另一个 .cpp 文件定义了 struct S { int y; };,则将它们连接到一起的程序的行为未定义。
  • 无名命名空间通常被用于解决这种问题。

ODR使用

非正式地说:

  • 一个对象在其值被读取(除非它是编译时常量)或写入,或其地址被取,或者被引用绑定时,这个对象被 ODR 使用。
  • 使用“所引用的对象在编译期未知”的引用时,这个引用被 ODR 使用。
  • 一个函数在被调用或其地址被取时,被 ODR 使用。

如果一个对象、引用或函数被 ODR 使用,则其定义必须存在于程序中的某处;否则常为连接时错误。

struct S {
    
    
    static const int x = 0; // 静态数据成员
    // 如果 ODR 使用它,就需要一个类外的定义
};
const int& f(const int& r);
 
int n = b ? (1, S::x) // S::x 此处并未 ODR 使用
          : f(S::x);  // S::x 此处被 ODR 使用:需要一个定义

猜你喜欢

转载自blog.csdn.net/zhizhengguan/article/details/114988629