静态const积分成员的基本乐趣

时间:2015-11-19 19:25:09

标签: c++ compiler-errors language-lawyer one-definition-rule

请考虑以下代码:

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

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

int main() {
}

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

有些人可能会惊讶于它不仅依赖于编译器,还依赖于其优化选项!例如,在gcc上,代码将拒绝与关闭的优化链接,但很乐意链接(并生成无法执行任务的可执行文件)与任何已启用的优化。

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

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

1 个答案:

答案 0 :(得分:3)

由于您odr-using i取其地址,因此必须在课堂外定义:

const int X::i ;

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

  

非正式地说,如果一个对象的地址被采用,或者一个对象被使用了   引用绑定到它,如果函数是函数,则函数使用odr   打电话给它或其地址。如果是对象或函数   使用得很多,它的定义必须存在于程序的某个地方;一个   违反这是一个链接时错误。

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

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

  

每个程序都应包含每个非内联的一个定义   在该程序中使用的函数或变量;没有诊断   需要

一些注意事项:

  • 一般来说,抓取odr违规行为是hard problem
  • 在这种情况下,捕捉它并不像看起来那么简单,因为i可以在以后或在另一个翻译单元中定义。
  • 我们想要行外定义,因为我们只需要一个定义,而声明可以重复,即如果它们是头文件的一部分。