是否使用了静态constexpr odr
Static constexpr odr-used or not?
为什么以下内容在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?
TryMe::arr
是odr使用的,但您没有提供定义(实时查看):
constexpr int TryMe::arr[1];
为什么gcc
和clang
的结果不一致?这是因为违反odr不需要从C++11和C++14标准草案(emphasis mine)中进行识别:
每个程序应包含每个非内联程序的一个定义该程序中使用的odr函数或变量无诊断必需。
我们可以从C++11标准草案3.2
中看到它是odr使用的,其中写道:
表达式可能被求值,除非它是未求值的操作数(第5条)或其子表达式。一个变量,其名称显示为可能求值的表达式时使用odr,除非满足出现在常数中的要求的对象表达式(5.19)和左值到右值的转换(4.1)为立即应用。
TryMe::arr
是一个对象,它确实满足出现在常量表达式中的要求,但左值到右值的转换并没有立即应用于TryMe::arr
,而是应用于TryMe::arr[0]
。
C++14标准草案的更新措辞也适用于C++11,因为它是通过缺陷报告(DR 712)应用的:
变量x,其名称显示为可能求值的表达式ex是odr,除非应用左值到右值的转换(4.1)到x产生一个常数表达式(5.19),该表达式不调用任何非平凡函数,如果x是对象,则ex是表达式e的一组潜在结果,其中左值到右值的转换(4.1)应用于e,或者e是丢弃值表达式
表达式TryMe::arr[0]
的潜在结果根据3.2
段落2
中的标准为空,因此使用它。
注意:您需要根据9.4.2
[class.static.data]部分提供类外的定义,其中表示(emphasis mine):
可以在类中声明文本类型的静态数据成员带有constexpr说明符的定义;如果是,其声明应指定一个大括号或相等的初始值设定项,其中的每个初始值设定值子句即赋值表达式是常量表达式。[注:在在这两种情况下,成员都可能出现在常量表达式中--终止注意]如果成员程序和命名空间范围定义中是否使用了odr(3.2)不应包含初始值设定项
更新
T.C.指出了1926年的缺陷报告,该报告在3.2
【basic.def.odr】第2段中添加了以下项目符号:
- 如果e是带数组操作数的下标运算(5.2.1[expr.sub]),则集合包含该操作数
这意味着下标数组不再是odr的用途,因此OP代码将在C++1z中很好地形成,它看起来像C++14,因为缺陷看起来像是针对C++14的。
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 多成员Constexpr结构初始化
- 条件constexpr函数
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- Visual C++ constexpr Hints
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 为什么constexpr的性能比正常表达式差
- 是否可以使用if constexpr删除控制流语句
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 为什么std::isnan 不是 constexpr?
- 是否使用静态 constexpr 变量 odr?
- C++ standard: ODR and constexpr std::string_view
- 具有静态constexpr成员的模板类的ODR
- c++14 static constexpr auto with odr usage
- 在内联和 constexpr 功能的情况下"obey ODR"意味着什么?
- 是否可以在编译时使用`constexpr std::initializer_list`,但不能使用ODR
- ODR-使用转发的 constexpr 参数
- 是否使用了静态constexpr odr
- 是否应该定义静态constexpr类成员变量,即使它们不是ODR使用的
- constexpr and ODR