为什么以下内容适用于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]
是一个对象吗?如果是的话,它是否经常使用?
答案 0 :(得分:8)
TryMe::arr
为odr-used,但您未提供定义( see it live ):
constexpr int TryMe::arr[1];
为什么gcc
和clang
之间的结果不一致?这是因为在C ++ 11和C ++ 14草案标准(强调我的)中,odr违规不需要不相关:
每个程序都应包含每个非内联的一个定义 在该程序中使用的函数或变量; 没有诊断 需要强>
我们可以看到它在C ++ 11标准草案3.2
部分中使用了更多信息,其中说:
表达式可能会被评估,除非它是未评估的 操作数(第5条)或其子表达式。一个名称的变量 似乎是一种可能被评估的表达式,除非它被使用 满足出现在常量中的要求的对象 表达式(5.19)和左值到右值的转换(4.1)是 立即申请。
TryMe::arr
是一个对象,它确实满足出现在常量表达式中的要求,但是左值到右值的转换不会立即应用于TryMe::arr
,而是应用于TryMe::arr[0]
。< / p>
C ++ 14标准草案中更新的措辞,适用于C ++ 11,因为它是通过缺陷报告( DR 712 )应用的:
变量x,其名称显示为可能已评估的表达式 除非应用左值到右值的转换,否则ex是odr-used(4.1) to x产生一个不调用any的常量表达式(5.19) 非平凡的函数,如果x是一个对象,ex是一个元素 一组表达式e的潜在结果,其中任何一个 左值到左值的转换(4.1)适用于e,或e是a 丢弃值表达
表达式TryMe::arr[0]
的潜在结果由3.2
段2
中的条件为空,因此使用率较高。
注意:您需要根据9.4.2
[class.static.data] 部分提供类外的定义,其中说明了(强调我的) :
可以在类中声明文字类型的静态数据成员 使用constexpr说明符定义;如果是的话,其声明应 指定一个brace-or-equal-initializer,其中包含每个initializer子句 这是一个赋值表达式是一个常量表达式。 [注意:在 在这两种情况下,成员都可能出现在常量表达式中。 -结束 note] 如果成员仍然在命名空间范围内定义 在程序和命名空间范围定义中使用odr-used(3.2) 不得包含初始化程序
更新
T.C。指出defect report 1926将以下项目符号添加到3.2
[basic.def.odr]第2段:
- 如果e是带有数组操作数的下标操作(5.2.1 [expr.sub]),则该集合包含该操作数。
这意味着下标一个数组不再是一个odr用途,所以OPs代码将在C ++ 1z中很好地形成,它看起来像C ++ 14,因为缺陷看起来像C ++ 14