【问题标题】:Static constexpr odr-used or not?static constexpr odr-used 与否?
【发布时间】:2014-11-29 12:34:52
【问题描述】:

为什么以下内容适用于gcc,但不适用于clang,(see it live):

constexpr int giveMeValue() { return 42; }

struct TryMe {
  static constexpr int arr[1] = {
      giveMeValue()
  };  
};

int main() {
    int val = TryMe::arr[0];
    return val;
}

我用 clang 得到一个未解析的外部符号。

TryMe::arr[0] 是一个对象吗?如果是,它是 odr-used 吗?

【问题讨论】:

标签: c++ c++11 constexpr one-definition-rule


【解决方案1】:

TryMe::arrodr-used 但您没有提供定义 (see it live):

constexpr int TryMe::arr[1];

为什么gccclang的结果不一致?这是因为 odr 违规不需要从 C++11 和 C++14 草案标准(强调我的)进行诊断:

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

我们可以从 C++11 标准草案中看到它是 odr-used,3.2 部分说:

一个表达式可能会被计算,除非它是一个未计算的 操作数(第 5 条)或其子表达式。一个变量,其名称 显示为潜在评估的表达式是 odr-used 除非它是 满足出现在常量中的要求的对象 表达式 (5.19) 和左值到右值的转换 (4.1) 是 立即申请。

TryMe::arr 是一个对象,它确实满足出现在常量表达式中的要求,但左值到右值的转换不会立即应用于TryMe::arr,而是应用于TryMe::arr[0]

C++14 标准草案的更新措辞也适用于 C++11,因为它是通过缺陷报告应用的 (DR 712):

变量 x,其名称显示为潜在求值表达式 除非应用左值到右值转换 (4.1),否则 ex 是 odr-used to x 产生一个不调用任何表达式的常量表达式 (5.19) 非平凡函数,如果 x 是一个对象,ex 是 表达式 e 的一组潜在结果,其中 左值到右值转换 (4.1) 应用于 e,或者 e 是 丢弃值表达式

根据3.2 段落2 中的条件,表达式TryMe::arr[0]潜在结果为空,因此它是odr-used。

注意:您需要根据9.4.2 [class.static.data] 部分在类之外提供定义(强调我的):

可以在类中声明文字类型的静态数据成员 使用 constexpr 说明符定义;如果是,其声明应 指定一个大括号或相等初始化器,其中每个初始化器子句 也就是说,赋值表达式是一个常量表达式。 [注:在 在这两种情况下,成员都可能出现在常量表达式中。 -结尾 note ] 该成员仍应在命名空间范围内定义,如果它 在程序和命名空间范围定义中被 odr-used (3.2) 不应包含初始化程序

更新

T.C.指出defect report 1926 将以下项目符号添加到3.2 [basic.def.odr] 第2 段:

  • 如果 e 是带有数组操作数的下标操作 (5.2.1 [expr.sub]),则集合包含该操作数。

这意味着下标数组不再是 odr 使用,因此 OP 代码在 C++1z 中格式正确,并且看起来像 C++14,因为缺陷看起来像是针对 C++14 .

【讨论】:

  • 关于声明与定义的更多说明。 OP 在他的TryMe 结构中的内容可能看起来是一个定义,但实际上是一个声明,因为关于何时声明也是定义的有趣规则。这里 OP 有一个带有类内成员初始化程序的 static 变量,这通常是不允许的,但由于 constexpr(和 int)在这里是允许的。但是,OP 实际上并没有为TryMe::arr 分配内存,因为变量也是static,它需要外部存储。因此,就像任何static 变量一样,OP 仍然必须在结构之外提供声明。
  • (继续之前的评论,因为我的空间用完了)。但是,在 OP 定义 TryMe 结构的同一文件中提供定义 constexpr int TryMe::arr[1]; 并不是一个好主意,因为任何想要包含结构 TryMe 的定义的类都将尝试为 TryMe::arr[1] 分配存储空间,违反了单一定义规则。因此,通常会发生的情况是,您会将 constexpr int TryMe::arr[1] 放在某种 .cpp 文件中,与标题分开。
  • @AndyG 公平点,我假设一些知识,我应该添加更详细的。让我添加更多细节。
  • 我觉得你的帖子很棒!我只是想为那些发生在它上面的潜伏者添加一些额外的细节,所以它不是在攻击你。
  • @AndyG 不用担心,我总是欢迎建设性的 cmets。你指出了我没想指出的重要细节。
猜你喜欢
  • 1970-01-01
  • 2016-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多