2

考虑以下代码:

struct X {
  static const int i = 45;
};

void foo() {
  const int* k = &X::i;
}

int main() {
}

如果不打开您最喜欢的编译器,您认为尝试编译和链接这个简单的野兽会产生什么结果?

有些人可能会感到惊讶,它不仅取决于编译器,还取决于它的优化选项!例如,在 gcc 上,代码将拒绝在优化关闭的情况下链接,但会很乐意在任何优化打开的情况下链接(并生成可运行的无操作可执行文件)。

失败案例中的诊断会很有趣 -X::i找不到符号。启用优化的链接会成功,因为X::i会被丢弃。

和问题。编译此代码的编译器行为是否正确?由于X::i没有链接,当被要求生成一个要求该符号链接的代码时,编译器不应该抱怨吗?

4

1 回答 1

3

由于您通过获取地址来使用 odr i,因此必须在类之外定义它:

const int X::i ;

违反此规则属于不需要诊断的类别,因此这是完全有效的行为:

非正式地,如果一个对象的地址被获取,或者一个引用被绑定到它,那么它就是 odr-used,如果一个函数被调用或者它的地址被获取,那么一个函数就是 odr-used。如果一个对象或一个函数被odr-used,它的定义必须存在于程序的某个地方;违反这一点是链接时错误。

这可能取决于编译器、优化级别等...

C++ 标准部分草案3.2[basic.def.odr] 说:

每个程序都应包含该程序中 odr 使用的每个非内联函数或变量的准确定义;无需诊断

一些注意事项:

  • 一般而言,捕捉 odr 违规行为是一个难题
  • 在这种情况下,捕捉它并不像看起来那么简单,因为i可以稍后或在另一个翻译单元中定义。
  • 因为我们只想要一个定义,所以我们想要不合规的定义,而声明可以重复,即如果它们是头文件的一部分。
于 2015-11-19T19:38:16.190 回答