编译器如何知道其他 .cpp 文件如何使用静态常量成员?

How does compiler know how other .cpp files use a static const member?

有人可以从最权威的 ISO C++ 常见问题解答中解释一下这个例子吗?代码如下:

// Fred.h
class Fred {
public:
  static const int maximum = 42;
  // ...
};

// Fred.cpp
#include "Fred.h"
const int Fred::maximum;
// ...

而我无法得到的声明是:

If you ever take the address of Fred::maximum, such as passing it by reference or explicitly saying &Fred::maximum, the compiler will make sure it has a unique address. If not, Fred::maximum won’t even take up space in your process’s static data area.

编译器单独处理 .cpp 文件,并且不知道其他文件如何处理当前正在处理的文件中定义的数据。那么,编译器如何决定它是否应该分配一个唯一地址?

原始项目在这里:https://isocpp.org/wiki/faq/ctors#static-const-with-initializers

编译器不决定任何事情。对于未定义 static class 成员的翻译单元,编译器生成的目标模块包含对符号的未解析引用。

当所有目标模块链接在一起时,链接器负责完成工作,并将所有未解析的引用从引用静态符号的翻译单元解析到定义了符号的单独翻译单元。

FAQ 条目说 const int Fred::maximum; 必须恰好在一个编译单元中定义。但是,只有当变量被程序 odr-used 时(例如,如果引用绑定到它),这才是正确的。

如果变量不是odr-used那么定义可以省略。

但是,如果变量实际上 odr-used 但没有定义,那么它是未定义的行为,不需要诊断。通常,如果需要变量的地址但省略了定义,那么质量好的 linker 会省略 "undefined reference" 错误。

但您并不总是希望依赖未定义行为的特定表现形式。因此,最好始终包含定义 const int Fred::maximum;.


您问题中引用的段落旨在解决潜在的程序员问题:"Well, can't I save 4 bytes in my static data area by omitting the definition in some cases?"

说 compiler/linker 可以 执行整个程序分析,并在确定定义不是用过的。

尽管行 const int Fred::maximum; 被定义为为 int 分配内存,但这是允许的优化,因为符合要求的程序无法衡量内存是否实际被分配分配给未 odr-used.

int

该 FAQ 条目的作者显然希望 compiler/linker 实际上会这样做。


标准中关于 odr-use 的措辞旨在支持 compilation/linking 的以下模型:

  • 如果某些代码要求变量有地址,目标文件将包含对变量的引用。
  • 优化可能会删除一些从未调用过的代码路径等
  • 在 link 时,解析这些引用时,该引用将绑定到变量的定义。

它不需要编译器生成 "undefined reference" 错误消息,因为这会使编译器更难优化。优化的另一个阶段可能会碰巧完全删除目标文件中包含引用的部分。例如,如果结果 odr-use 只出现在一个从未被调用的函数中。