10

为什么以下内容有效gcc但无效clang,(现场观看):

constexpr int giveMeValue() { return 42; }

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

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

我得到了一个未解析的外部符号,带有铿锵声。

TryMe::arr[0]对象吗?如果是,它是 odr 使用的吗?

4

1 回答 1

9

TryMe::arr使用 odr,但您没有提供定义(现场查看):

constexpr int TryMe::arr[1];

gcc为什么和之间的结果不一致clang?这是因为 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 其名称显示为潜在求值表达式 ex 是 odr-used 除非将左值到右值转换 (4.1) 应用到 x 产生一个不调用任何非平凡函数的常量表达式 (5.19) 并且,如果x 是一个对象,ex 是表达式 e 的潜在结果集合中的一个元素,其中左值到右值转换 (4.1) 应用于 e,或者 e 是丢弃值表达式

根据段落中的标准,表达式的潜在结果TryMe::arr[0]是空的,因此它是 odr 使用的。3.22

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

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

更新

TC 指出缺陷报告 1926将以下项目符号添加到3.2[basic.def.odr] 第 2 段:

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

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

于 2015-02-11T04:01:10.993 回答