在应用间接引用时,标准是否要求指针变量的左值到右值转换?
TL;DR
给定以下代码:
int* ptr;
*ptr = 0;
在应用间接引用之前,*ptr
是否需要 lvalue-to-rvalue 的 ptr
转换?
does *ptr
require an lvalue-to-rvalue conversion of ptr
before applying indirection?
该标准在很多地方涵盖了 lvalue-to-rvalue 的主题,但似乎没有指定足够的信息来确定 * 运算符 是否需要这种转换.
The standard covers the topic of lvalue-to-rvalue in many places but does not seem to specify enough information to determine whether the * operator require such a conversion.
详情
左值到右值的转换见N3485 在 4.1
Lvalue-to-rvalue conversion 段落 1 中说(强调我的未来):
The lvalue-to-rvalue conversion is covered in N3485 in section 4.1
Lvalue-to-rvalue conversion paragraph 1 and says (emphasis mine going forward):
可以转换非函数、非数组类型 T 的泛左值 (3.10)到一个纯右值. 53 如果 T 是一个不完整的类型,一个程序需要这种转换是格式错误的.如果对象泛左值指的不是 T 类型的对象,也不是 a 类型的对象派生自 T 的类型,或者如果对象未初始化,则为程序需要进行此转换的行为具有未定义的行为.[...]
A glvalue (3.10) of a non-function, non-array type T can be converted to a prvalue.53 If T is an incomplete type, a program that necessitates this conversion is ill-formed. If the object to which the glvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.[...]
*ptr = 0;
需要这种转换吗?
如果我们转到 4
段 1 它说:
If we go to section 4
paragraph 1 it says:
[...]一个标准的转换序列将应用于一个表达式如有必要将其转换为所需的目标类型.
[...]A standard conversion sequence will be applied to an expression if necessary to convert it to a required destination type.
那么什么时候需要?如果我们查看 5
Expressions 部分,lvalue-to-rvalue 转换在段落 9 中提到:
So when is it necessary? If we look at section 5
Expressions the lvalue-to-rvalue conversion is mentioned in paragraph 9 which says:
每当泛左值表达式作为运算符的操作数出现时期望该操作数的纯右值,即左值到右值(4.1),数组到指针 (4.2) 或函数到指针 (4.3) 标准应用转换将表达式转换为纯右值.[...]
Whenever a glvalue expression appears as an operand of an operator that expects a prvalue for that operand, the lvalue-to-rvalue (4.1), array-to-pointer (4.2), or function-to-pointer (4.3) standard conversions are applied to convert the expression to a prvalue. [...]
和段落 11 说:
在某些情况下,表达式的出现只是为了它的副作用.这样的表达式称为丢弃值表达式.[...]左值到右值转换 (4.1) 当且仅当表达式是一个 volatile 限定类型的左值,它是以下[...]
In some contexts, an expression only appears for its side effects. Such an expression is called a discarded-value expression.[...] The lvalue-to-rvalue conversion (4.1) is applied if and only if the expression is an lvalue of volatile-qualified type and it is one of the following [...]
这两个段落似乎都不适用于此代码示例和 5.3.1
一元运算符 段落 1 它说:
neither paragraph seems to apply to this code sample and 5.3.1
Unary operators paragraph 1 it says:
一元 * 运算符执行间接:它所指向的表达式被应用的应该是一个指向对象类型的指针,或者指向一个对象类型的指针函数类型,结果是引用对象的左值或表达式指向的函数.如果表达式的类型是指向 T 的指针",结果的类型是T".[注:间接通过指向不完整类型(cv void 除外)的指针是有效的.这样获得的左值可以以有限的方式使用(初始化一个参考,例如);此左值不得转换为右值,见 4.1.――结尾说明]
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is "pointer to T," the type of the result is "T." [ Note: indirection through a pointer to an incomplete type (other than cv void) is valid. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to a prvalue, see 4.1. ―end note ]
它似乎不需要指针的值,而且我没有看到任何指针转换的要求,我是否遗漏了什么?
it does not seem to require the value of the pointer and I don't see any requirements for a conversion of the pointer here am I missing something?
我们为什么关心?
我在其他问题中看到了一个答案和评论,声称使用未初始化的指针是未定义的行为,因为需要 lvalue-to-rvalue 转换 ptr
在应用间接之前.例如:C++ 标准究竟在哪里说取消引用一个未初始化的指针是未定义的行为? 提出了这个论点,我无法将这个论点与任何最近的标准草案版本中的布局相协调.由于我已经多次看到这个,我想得到澄清.
I have seen an answer and comments in other questions that claim the use of an uninitialized pointer is undefined behavior due the need for an lvalue-to-rvalue conversion of ptr
before applying indirection. For example: Where exactly does C++ standard say dereferencing an uninitialized pointer is undefined behavior? makes this argument and I can not reconcile the argument with what is laid out in any of the recent draft versions of the standard. Since I have seen this several times I wanted to get clarification.
未定义行为的实际证据并不重要,因为正如我在上面链接的问题中指出的那样,我们有其他方法可以达到未定义行为.
The actual proof of undefined behavior is not as important since as I noted in the linked question above we have others way to get to undefined behavior.
推荐答案
我已将问题中的更新部分转换为答案,因为此时它似乎是答案,尽管我的问题无法回答令人不满意:
I have converted the update section in my question to an answer since at this point it seems to be the answer, albeit an unsatisfactory one that my question is unanswerable:
dyp 向我指出了两个涵盖非常相似基础的相关主题:
dyp pointed me to two relevant threads that cover very similar ground:
- 什么是未指定时 C++ 运算符的操作数的值类别?
- 初始化是否需要左值到-右值转换?是 int x = x;优?
共识似乎是该标准不明确,因此无法提供我正在寻找的答案,约瑟夫曼斯菲尔德 发布了一份关于缺乏规范的缺陷报告,看起来仍然是打开,不清楚什么时候可以澄清.
The consensus seems to be that the standard is ill-specified and therefore can not provide the answer I am looking for, Joseph Mansfield posted a defect report on this lack of specification, and it looks like it is still open and it is not clear when it may be clarified.
对于标准的意图,有一些常识性的论据.可以争论 逻辑上,如果操作需要使用该操作数的值,则该操作数为纯右值.另一个论点是,如果我们回顾一下 C99 草案标准说默认情况下会进行左值到右值的转换 并注明了例外情况.C99 标准草案的相关部分是 6.3.2.1
Lvalues, arrays, and function designators 段落 2 说:
There are a few common sense arguments to be made as to the intent of the standard. One can argue Logicially, an operand is a prvalue if the operation requires using the value of that operand. Another argument is that if we look back to the C99 draft standard says an lvalue to rvalue conversion is done by default and the exceptions are noted. The relevant section from the draft C99 standard is 6.3.2.1
Lvalues, arrays, and function designators paragraph 2 which says:
除非是sizeof运算符的操作数,否则一元&运算符、++ 运算符、-- 运算符或 .运算符或赋值运算符,没有数组类型的左值被转换为存储在指定对象中的值(不再是左值).[…]
Except when it is the operand of the sizeof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue). […]
基本上说,除了一些例外,操作数被转换为存储的值,并且由于间接不是例外,如果这在C++ 那么它确实可以回答我的问题是.
which basically says with some exceptions an operand is converted to the value stored and since indirection is not an exception if this is clarified to also be the case in C++ as well then it would indeed make the answer to my question yes.
当我试图澄清未定义行为的证据时,不如澄清是否强制要求进行左值到右值转换重要.如果我们想证明未定义的行为,我们有其他方法.Jerry 的方法是一种常识性方法,其中间接要求表达式是指向对象或函数的指针,而不确定的值只会偶然指向有效对象.一般来说,C++ 标准草案没有明确声明说使用不确定值是未定义的,这与 C99 草案标准不同 在 C++11 和后面的标准中没有给出明确声明说使用不确定的值是不确定的.迭代器和扩展指针除外,我们确实有奇异值的概念,并且我们在 24.2.1
部分被告知:
As I attempted to clarify the proof of undefined behavior was less important than clarifying whether a lvalue-to-rvalue conversion is mandated. If we want to prove undefined behavior we have alternate approaches. Jerry’s approach is a common sense one and in that indirection requires that the expression be a pointer to an object or function and an indeterminate value will only by accident point to a valid object. In general the draft C++ standard does not give an explicit statement to say using an indeterminate value is undefined, unlike the C99 draft standard In C++11 and back the standard does not give an explicit statement to say using an indeterminate value is undefined. The exception being iterators and by extension pointers we do have the concept of singular value and we are told in section 24.2.1
that:
[…][ 示例:在声明未初始化的指针 x 之后(与 int* x; 一样),必须始终假定 x 具有指针的奇异值.―结束示例] […] 可解引用的值总是非奇异的.
[…][ Example: After the declaration of an uninitialized pointer x (as with int* x;), x must always be assumed to have a singular value of a pointer. ―end example ] […] Dereferenceable values are always non-singular.
和:
无效迭代器是指可能是单数的迭代器.268
An invalid iterator is an iterator that may be singular.268
和脚注 268 说:
这个定义适用于指针,因为指针是迭代器.取消引用已失效的迭代器的效果是不确定的.
This definition applies to pointers, since pointers are iterators. The effect of dereferencing an iterator that has been invalidated is undefined.
在 C++1y ??语言发生了变化,我们确实有一个明确的声明,使用未定义的中间值,但有一些狭隘的例外.
相关文章