TL; DR
给出以下代码:
int* ptr;
*ptr = 0;
在申请间接费用之前, *ptr
是否要求ptr
进行左值到右值转换?
该标准涵盖了许多地方左值到右值的主题,但似乎没有指定足够的信息来确定 *运算符是否需要这样的转换。
详细
左值到右值转换在第4.1
左值到右值转换段 1的N3485中有所介绍并说(强调我的前进):
可以转换非函数非数组类型T的glvalue(3.10) 如果T是一个不完整的类型,那么该程序就是一个prvalue 需要这种转换是不正确的。如果对象是哪个 glvalue refer不是T类型的对象,也不是a的对象 类型派生自T,或者如果对象未初始化,则为程序 这需要转换具有未定义的行为。[...]
*ptr = 0;
是否需要进行此转换??
如果我们转到4
段 1 ,则说:
[...]标准转换序列将应用于表达式 如有必要将其转换为所需的目标类型。
那么什么时候必要?如果我们查看5
表达式部分,则 9 段中会提到左值到右值转换,其中包含:
每当glvalue表达式作为运算符的操作数出现时 期望该操作数的prvalue,左值到右值(4.1), 数组到指针(4.2)或函数到指针(4.3)标准 应用转换以将表达式转换为prvalue。 [...]
和段 11 表示:
在某些情况下,表达式仅出现其副作用。 这样的表达式称为丢弃值表达式。[...] 当且仅当时,应用左值到右值转换(4.1) expression是volatile限定类型的左值,它是其中之一 以下[...]
这两段似乎都不适用于此代码示例和5.3.1
一元运算符段 1 它说:
一元*运算符执行间接:它所表达的表达式 应用应该是指向对象类型的指针,或指向a的指针 函数类型,结果是引用对象或的左值 表达式指向的函数。如果是表达式的类型 是“指向T的指针”,结果的类型是“T”。[注意:间接 通过指向不完整类型(cv void除外)的指针是有效的。 由此获得的左值可以以有限的方式使用(以初始化a 参考,例如);此左值不得转换为 prvalue,见4.1。 - 后注]
它似乎不需要指针的值,我没有看到指针转换的任何要求,我错过了什么?
我们为什么关心?
我在其他问题中看到了答案和评论,声称使用未初始化的指针是未定义的行为,因为在应用之前需要对ptr
进行左值到右值的转换间接。例如:Where exactly does C++ standard say dereferencing an uninitialized pointer is undefined behavior?提出这个论点,我无法将该论证与该标准的任何最新草案版本中的内容进行协调。自从我多次看到这个以来,我想澄清一下。
未定义行为的实际证明并不重要,因为正如我在上面的链接问题中所指出的那样,我们还有其他方法可以解决未定义的行为。
答案 0 :(得分:13)
我认为你是从一个相当倾斜的角度接近这个,可以这么说。根据§5.3.1/ 1:
一元
*
运算符执行间接:应用它的表达式应该是指向它的指针 对象类型或指向函数类型的指针,结果是引用对象或函数的左值 表达点所指向的。如果表达式的类型是“指向T的指针”,则结果的类型为“T”。
虽然这没有讨论左值到右值的转换,但它要求表达式是指向对象或函数的指针。未初始化的指针不会(除非,可能是偶然的)是任何此类事情,因此解除引用的尝试会给出未定义的行为。
答案 1 :(得分:4)
我已将我的问题中的更新部分转换为答案,因为在这一点上它似乎是答案,尽管我的问题无法回答是一个令人不满意的部分:
dyp向我指出了两个涵盖非常类似的相关主题:
共识似乎是标准指定,因此无法提供我想要的答案,Joseph Mansfield posted a defect report on this lack of specification,它看起来像它仍然open并且不清楚何时可以澄清。
对于标准的 intent ,有一些常识性的论据。人们可以争辩Logicially, an operand is a prvalue if the operation requires using the value of that operand。另一个论点是,如果我们回顾C99 draft standard说an lvalue to rvalue conversion is done by default并注意到例外情况。 C99标准草案的相关部分是6.3.2.1
Lvalues,数组和函数指示符段 2 ,其中包含:
除非它是sizeof运算符的操作数,否则一元&运算符,++运算符, - 运算符或者左运算符。运算符或赋值运算符, 没有数组类型的左值被转换为存储在指定对象中的值(并且不再是左值)。 [...]
基本上说除了一些例外,操作数被转换为存储的值,因为间接不是例外,如果明白这一点也是的情况> C ++ 然后它确实会回答我的问题是。
当我试图澄清未定义行为的证据不如澄清是否强制执行左值到右值的转换。如果我们想要证明未定义的行为,我们有其他方法。 Jerry的方法是常识,并且间接要求表达式是指向对象或函数的指针,不确定的值只会偶然指向有效对象。 一般来说,草案C ++标准没有给出一个明确的陈述,说使用不确定的值是未定义的,不像C99草案标准在C ++ 11中,并且标准中没有给出明确的说法使用不确定的值是未定义的。除了迭代器和扩展指针之外,我们确实有奇异值的概念,我们在24.2.1
部分告诉我们:
[...] [示例:在声明未初始化的指针x(与int * x;一样)之后,必须始终假定x具有指针的奇异值。 -end example] [...]可解除引用的值总是非单数的。
和
无效的迭代器是一个可能是单数的迭代器。 268
和脚注268说:
此定义适用于指针,因为指针是迭代器。取消引用已失效的迭代器的效果未定义。