在 C++14 标准 (n3797) 中,关于左值到右值转换的部分内容如下(强调我的):
4.1 左值到右值的转换[conv.lval]
非函数、非数组类型的 glvalue (3.10)
T
可以转换为纯右值。如果T
是不完整类型,则需要进行此转换的程序格式错误。如果T
是非类类型,则纯右值的类型是 的 cv 非限定版本T
。否则纯右值的类型是T
。当在未计算的操作数或其子表达式中发生左值到右值的转换(第 5 条)时,不访问包含在引用对象中的值。在所有其他情况下,转换结果根据以下规则确定:
- 如果
T
是一个(可能是 cv 限定的)std::nullptr_t
,那么结果是一个空指针常量。- 否则,如果
T
具有类类型,则转换复制初始化T
来自泛左值的临时类型,并且转换的结果是临时的纯右值。- 否则,如果泛泛值引用的对象包含无效的指针值,则行为是实现定义的。
- 否则,如果
T
是(可能是 cv 限定的)无符号字符类型,并且泛左值所引用的对象包含不确定值,并且该对象没有自动存储持续时间,或者泛左值是一元运算符的操作数,&
或者它是绑定到一个引用,结果是一个未指定的值。- 否则,如果泛左值引用的对象具有不确定的值,则行为未定义。
- 否则,glvalue 指示的对象是纯右值结果。
- [注:另见 3.10]
这一段的意义是什么(粗体)?
如果这一段不在这里,那么它适用的情况将导致未定义的行为。通常,我希望unsigned char
在具有不确定值的情况下访问一个值会导致未定义的行为。但是,这一段意味着
- 如果我实际上并没有访问字符值,即我立即将它传递给
&
或将其绑定到引用,或者 - 如果
unsigned char
没有自动存储期限,
然后转换产生一个未指定的值,而不是未定义的行为。
我是否正确得出以下结论:该程序:
#include <new>
#include <iostream>
// using T = int;
using T = unsigned char;
int main() {
T * array = new T[500];
for (int i = 0; i < 500; ++i) {
std::cout << static_cast<int>(array[i]) << std::endl;
}
delete[] array;
}
由标准明确定义,并且必须输出 500 个未指定整数的序列,而同一个程序 whereT = int
会有未定义的行为?
IIUC,使其 UB 读取具有不确定值的内容的原因之一是允许优化器积极消除死存储。因此,本段可能意味着符合标准的编译器在unsigned char
使用unsigned char
.
假设我理解正确,这条规则的基本原理是什么?何时能够读取unsigned char
具有不确定值并获得未指定结果而不是 UB 有用?我有这样的感觉,如果他们在制定这部分规则时付出了这么多努力,他们就有动力帮助他们关心的某些代码示例,或者与标准的其他部分保持一致,或者简化其他问题. 但我不知道那可能是什么。