静态constexpr odr-used or not?

Dea*_*ean 9 c++ one-definition-rule constexpr c++11

为什么下面的工作原理gcc却没有clang,(现场观看):

constexpr int giveMeValue() { return 42; }

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

int main() {
    int val = TryMe::arr[0];
    return val;
}
Run Code Online (Sandbox Code Playgroud)

我得到一个未解决的外部符号与clang.

TryMe::arr[0]一个对象?如果是的话,它是否经常使用?

Sha*_*our 9

TryMe::arrODR使用的,但你没有提供一个定义(见直播):

constexpr int TryMe::arr[1];
Run Code Online (Sandbox Code Playgroud)

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

每个程序应该只包含该程序中使用的每个非内联函数或变量的一个定义; 无需诊断.

我们可以看到它在C++ 11标准草案中的使用情况,该部分3.2说:

除非是未评估的操作数(第5条)或其子表达式,否则可能会对表达式进行求值.名称显示为潜在评估表达式的变量是odr-used,除非它是满足出现在常量表达式(5.19)中的要求的对象,并且立即应用左值到右值转换(4.1).

TryMe::arr是一个对象,它并满足要求为出现在一个恒定表达,但左值到右值转换未立即应用于TryMe::arr而是TryMe::arr[0].

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

变量x的名称显示为潜在评估的表达式ex,除非将lvalue-to-rvalue转换(4.1)应用于x,否则将生成不调用任何非平凡函数的常量表达式(5.19),并且如果x是一个对象,ex是表达式e的潜在结果集合的元素,其中左值到右值的转换(4.1)应用于e,或者e是丢弃值表达式

该表达式的潜在结果TryMe::arr[0]3.2段落中的标准是空的2,因此它是使用的.

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

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

更新

TC指出了缺陷报告1926,它将以下子弹添加到3.2[basic.def.odr]第2段:

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

这意味着下标数组不再是一种使用方法,因此OPs代码在C++ 1z中可以很好地形成,看起来像C++ 14,因为缺陷看起来像C++ 14.