未定义的行为和重新加载的序列点
将此主题视为以下主题的续集:
上一期
未定义的行为和序列点
让我们重温一下这个有趣和令人费解的事情 表达式(斜体短语取自上述主题 *smile* ):
i += ++i;
我们说这会调用未定义行为。我认为,当这么说时,我们隐含地假设 i
的 type 是内置类型之一。
如果i
的type是用户定义的类型怎么办?假设它的类型是 Index
,它在本文后面定义(见下文)。它仍然会调用未定义的行为吗?
如果是,为什么?它不等于编写 i.operator+=(i.operator++());
甚至语法上更简单的 i.add(i.inc());
吗?或者,它们也调用未定义的行为吗?
如果不是,为什么不呢?毕竟,对象i
在连续的序列点之间被修改两次。请回忆一下经验法则:表达式可以修改对象的值在连续的“序列点之间仅存在一次。如果i += ++i
是一个表达式,那么它必须调用未定义行为。如果是这样,那么它的等价物i.operator+=(i.operator++());
和 i.add(i.inc());
还必须调用似乎不真实的未定义行为(如!据我了解)
或者,i += ++i
不是一个表达式 如果是的话,那么它是什么以及的定义是什么? >表达式?
如果它是一个表达式,并且同时它的行为也定义良好,那么它意味着与表达式关联的序列点的数量在某种程度上取决于表达式中涉及的操作数的类型正确吗(即使是部分正确)?
顺便说一下,这个表达式怎么样?
//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the subscript operator!
a[++i] = i; //Taken from the previous topic. But here type of `i` is Index.
您也必须在响应中考虑这一点(如果您确定知道它的行为)。 :-)
吗
++++++i;
在 C++03 中定义明确 ?毕竟这就是这,
((i.operator++()).operator++()).operator++();
class Index
{
int state;
public:
Index(int s) : state(s) {}
Index& operator++()
{
state++;
return *this;
}
Index& operator+=(const Index & index)
{
state+= index.state;
return *this;
}
operator int()
{
return state;
}
Index & add(const Index & index)
{
state += index.state;
return *this;
}
Index & inc()
{
state++;
return *this;
}
};
Consider this topic a sequel of the following topic:
Previous installment
Undefined behavior and sequence points
Let's revisit this funny and convoluted expression (the italicized phrases are taken from the above topic *smile* ):
i += ++i;
We say this invokes undefined-behavior. I presume that when say this, we implicitly assume that type of i
is one of the built-in types.
What if the type of i
is a user-defined type? Say its type is Index
which is defined later in this post (see below). Would it still invoke undefined-behavior?
If yes, why? Is it not equivalent to writing i.operator+=(i.operator++());
or even syntactically simpler i.add(i.inc());
? Or, do they too invoke undefined-behavior?
If no, why not? After all, the object i
gets modified twice between consecutive sequence points. Please recall the rule of thumb: an expression can modify an object's value only once between consecutive "sequence points. And if i += ++i
is an expression, then it must invoke undefined-behavior. If so, then its equivalents i.operator+=(i.operator++());
and i.add(i.inc());
must also invoke undefined-behavior which seems to be untrue! (as far as I understand)
Or, i += ++i
is not an expression to begin with? If so, then what is it and what is the definition of expression?
If it's an expression, and at the same time, its behavior is also well-defined, then it implies that the number of sequence points associated with an expression somehow depends on the type of operands involved in the expression. Am I correct (even partly)?
By the way, how about this expression?
//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the subscript operator!
a[++i] = i; //Taken from the previous topic. But here type of `i` is Index.
You must consider this too in your response (if you know its behavior for sure). :-)
Is
++++++i;
well-defined in C++03? After all, this is this,
((i.operator++()).operator++()).operator++();
class Index
{
int state;
public:
Index(int s) : state(s) {}
Index& operator++()
{
state++;
return *this;
}
Index& operator+=(const Index & index)
{
state+= index.state;
return *this;
}
operator int()
{
return state;
}
Index & add(const Index & index)
{
state += index.state;
return *this;
}
Index & inc()
{
state++;
return *this;
}
};
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
看起来代码
在序列点方面工作得非常好。 C++ ISO 标准的第 1.9.17 节对序列点和函数求值进行了如下说明:
例如,这表明作为
operator +=
参数的 i.operator ++() 在求值后有一个序列点。简而言之,由于重载运算符是函数,因此适用正常的排序规则。顺便说一句,好问题!我真的很喜欢你强迫我理解一种我已经认为我知道的语言的所有细微差别(并且认为我认为我知道)。 :-)
It looks like the code
Works perfectly fine with regards to sequence points. Section 1.9.17 of the C++ ISO standard says this about sequence points and function evaluation:
This would indicate, for example, that the
i.operator ++()
as the parameter tooperator +=
has a sequence point after its evaluation. In short, because overloaded operators are functions, the normal sequencing rules apply.Great question, by the way! I really like how you're forcing me to understand all the nuances of a language that I already thought I knew (and thought that I thought that I knew). :-)
http://www.eelis.net/C++/analogliterals.xhtml
我想到了模拟文字
http://www.eelis.net/C++/analogliterals.xhtml
Analog literals comes to my mind
正如其他人所说,您的
i += ++i
示例适用于用户定义的类型,因为您正在调用函数,并且函数包含序列点。另一方面,假设
a
是您的基本数组类型,甚至是用户定义的数组类型,则a[++i] = i
就没那么幸运了。这里遇到的问题是我们不知道首先计算包含i
的表达式的哪一部分。可能是++i
被评估,传递给operator[]
(或原始版本),以便检索那里的对象,然后的值code>i
被传递给它(这是在i
递增之后)。另一方面,也许后者首先被求值,存储起来供以后赋值,然后++i
部分被求值。As others have said, your
i += ++i
example works with the user-defined type since you're calling functions, and functions comprise sequence points.On the other hand,
a[++i] = i
is not so lucky assuming thata
is your basic array type, or even a user defined one. The problem you've got here is that we don't know which part of the expression containingi
is evaluated first. It could be that++i
is evaluated, passed off tooperator[]
(or the raw version) in order to retrieve the object there, and then the value ofi
gets passed to that (which is afteri
was incremented). On the other hand, perhaps the latter side is evaluated first, stored for later assignment, and then the++i
part is evaluated.我认为它定义明确:
来自 C++ 草案标准 (n1905) §1.9/16:
请注意我加粗的部分。这意味着在增量函数调用 (
i.operator ++()
) 之后、在复合赋值调用 (i.operator+= )。
I think it's well-defined:
From the C++ draft standard (n1905) §1.9/16:
Note the part I bolded. This means there is indeed a sequence point after the increment function call (
i.operator ++()
) but before the compound assignment call (i.operator+=
).好吧。在查看了之前的回复后,我重新思考了我自己的问题,特别是只有诺亚尝试过的这部分 回答但我不完全相信他。
情况1:
如果
a
是一个内置类型的数组。那么诺亚说的就是对的。那是,因此,
a[++i]=i
调用未定义行为,或者结果未指定。不管是什么,它都没有明确的定义!PS:在上面的引用中,
删除线当然是我的。情况 2:
如果
a
是重载operator[]
的用户定义类型的对象,则同样有两种情况。operator[]
函数的返回类型是内置类型,则a[++i]=i
再次调用未定义行为或结果未指定。operator[]
函数的返回类型是用户定义的类型,那么a[++i] = i
的行为是明确定义的(到目前为止据我了解),因为在这种情况下a[++i]=i
相当于编写a.operator[](++i).operator=(i);
a.operator[](++i).operator=(i); 与a[++i].operator=(i);
相同。也就是说,赋值operator=
在a[++i]
的返回对象上调用,这似乎是非常明确定义的,因为当a[++i]
返回时,++i
已经被求值,然后返回对象调用运算符=
函数将i
的更新值作为参数传递给它。 请注意,这两个调用之间有一个序列点。并且语法确保这两个调用之间不存在竞争,并且operator[]将首先被调用,并且连续地,传递给它的参数++i
将被调用也先得到评价。可以将其视为
someInstance.Fun(++k).Gun(10).Sun(k).Tun();
,其中每个连续函数调用都会返回某种用户定义类型的对象。对我来说,这种情况更像是这样:eat(++k);drink(10);sleep(k)
,因为在这两种情况下,每个函数调用之后都存在序列点。如果我错了,请纠正我。 :-)
Alright. After going through previous responses, I re-thought about my own question, particularly this part that only Noah attempted to answer but I'm not convinced with him completely.
Case 1:
If
a
is an array of built-in type. Then what Noah said is correct. That is,So
a[++i]=i
invokes undefined-behavior, or the result is unspecified. Whatever it is, it's not well-defined!PS: In above quotation,
strike-throughis of course mine.Case 2:
If
a
is an object of user-defined type which overloads theoperator[]
, then again there are two cases.operator[]
function is built-in type, then againa[++i]=i
invokes undefined-behavior or the result is unspecified.operator[]
function is user-defined type, then the behavior ofa[++i] = i
is well-defined (as far as I understand), since in this casea[++i]=i
is equivalent to writinga.operator[](++i).operator=(i);
which is same as,a[++i].operator=(i);
. That is, assignmentoperator=
gets invoked on the returned object ofa[++i]
, which seems be very well-defined, since by the timea[++i]
returns,++i
have already been evaluated, and then the returned object callsoperator=
function passing the updated value ofi
to it as argument. Note that there is a sequence point between these two calls. And the syntax ensures that there is no competition between these two calls, andoperator[]
would get invoked first, and consecutively, the argument++i
passed into it, would also get evaluated first.Think of this as
someInstance.Fun(++k).Gun(10).Sun(k).Tun();
in which each consecutive function call returns an object of some user-defined type. To me, this situation seems more like this:eat(++k);drink(10);sleep(k)
, because in both situations, there exists sequence point after each function call.Please correct me if I'm wrong. :-)