未定义的行为和重新加载的序列点

发布于 2024-10-11 05:46:21 字数 2242 浏览 2 评论 0原文

将此主题视为以下主题的续集:

上一期
未定义的行为和序列点

让我们重温一下这个有趣令人费解的事情 表达式(斜体短语取自上述主题 *smile* ):

i += ++i;

我们说这会调用未定义行为。我认为,当这么说时,我们隐含地假设 itype 是内置类型之一。

如果itype是用户定义的类型怎么办?假设它的类型是 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

转瞬即逝 2024-10-18 05:46:21

看起来代码

i.operator+=(i.operator ++());

在序列点方面工作得非常好。 C++ ISO 标准的第 1.9.17 节对序列点和函数求值进行了如下说明:

当调用函数时(无论函数是否内联),在所有函数参数(如果有)的计算之后有一个序列点,该序列点发生在函数体中的任何表达式或语句执行之前。在复制返回值之后和执行函数外部的任何表达式之前还有一个序列点。

例如,这表明作为 operator += 参数的 i.operator ++() 在求值后有一个序列点。简而言之,由于重载运算符是函数,因此适用正常的排序规则。

顺便说一句,好问题!我真的很喜欢你强迫我理解一种我已经认为我知道的语言的所有细微差别(并且认为我认为我知道)。 :-)

It looks like the code

i.operator+=(i.operator ++());

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:

When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body. There is also a sequence point after the copying of a returned value and before the execution of any expressions outside the function.

This would indicate, for example, that the i.operator ++() as the parameter to operator += 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). :-)

黑白记忆 2024-10-18 05:46:21

http://www.eelis.net/C++/analogliterals.xhtml
我想到了模拟文字

  unsigned int c = ( o-----o
                     |     !
                     !     !
                     !     !
                     o-----o ).area;

  assert( c == (I-----I) * (I-------I) );

  assert( ( o-----o
            |     !
            !     !
            !     !
            !     !
            o-----o ).area == ( o---------o
                                |         !
                                !         !
                                o---------o ).area );

http://www.eelis.net/C++/analogliterals.xhtml
Analog literals comes to my mind

  unsigned int c = ( o-----o
                     |     !
                     !     !
                     !     !
                     o-----o ).area;

  assert( c == (I-----I) * (I-------I) );

  assert( ( o-----o
            |     !
            !     !
            !     !
            !     !
            o-----o ).area == ( o---------o
                                |         !
                                !         !
                                o---------o ).area );
满地尘埃落定 2024-10-18 05:46:21

正如其他人所说,您的 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 that a 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 containing i is evaluated first. It could be that ++i is evaluated, passed off to operator[] (or the raw version) in order to retrieve the object there, and then the value of i gets passed to that (which is after i was incremented). On the other hand, perhaps the latter side is evaluated first, stored for later assignment, and then the ++i part is evaluated.

画离情绘悲伤 2024-10-18 05:46:21

我认为它定义明确:

来自 C++ 草案标准 (n1905) §1.9/16:

“后面还有一个序列点
返回值的复制和
在执行任何操作之前
函数外部的表达式13) .
C++ 中的几个上下文导致
函数调用的评估,甚至
虽然没有相应的函数调用
语法出现在翻译中
单元。 [示例:评估新的
表达式调用一个或多个
分配和构造函数;
见 5.3.4。再举个例子,
调用转换函数
(12.3.2) 可能出现在
其中没有出现函数调用语法。
结束示例 ] 序列指向
函数入口和函数出口(如
如上所述)是特征
函数调用被评估,无论如何
表达式的语法

调用该函数可能是。 ”

请注意我加粗的部分。这意味着在增量函数调用 (i.operator ++()) 之后、在复合赋值调用 (i.operator+= )。

I think it's well-defined:

From the C++ draft standard (n1905) §1.9/16:

"There is also a sequence point after
the copying of a returned value and
before the execution of any
expressions outside the function13) .
Several contexts in C++ cause
evaluation of a function call, even
though no corresponding function call
syntax appears in the translation
unit. [ Example: evaluation of a new
expression invokes one or more
allocation and constructor functions;
see 5.3.4. For another example,
invocation of a conversion function
(12.3.2) can arise in contexts in
which no function call syntax appears.
end example ] The sequence points at
function-entry and function-exit (as
described above) are features of the
function calls as evaluated, whatever
the syntax of the expression
that
calls the function might be. "

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+=).

韬韬不绝 2024-10-18 05:46:21

好吧。在查看了之前的回复后,我重新思考了我自己的问题,特别是只有诺亚尝试过的这部分 回答但我不完全相信他。

a[++i] = i;

情况1:

如果a是一个内置类型的数组。那么诺亚说的就是对的。那是,

a[++i] = i 就没那么幸运了
a 是你的基本数组类型,
甚至用户定义了一个
。问题
你所面临的是我们所不知道的
表达式的哪一部分
首先评估包含 i 的内容。

因此,a[++i]=i 调用未定义行为,或者结果未指定。不管是什么,它都没有明确的定义!

PS:在上面的引用中,删除线当然是我的。

情况 2:

如果 a 是重载 operator[] 的用户定义类型的对象,则同样有两种情况。

  1. 如果重载operator[]函数的返回类型是内置类型,则a[++i]=i再次调用未定义行为或结果未指定。
  2. 但如果重载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.

a[++i] = i;

Case 1:

If a is an array of built-in type. Then what Noah said is correct. That is,

a[++i] = i is not so lucky assuming
that a 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
containing i is evaluated first.

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-through is of course mine.

Case 2:

If a is an object of user-defined type which overloads the operator[], then again there are two cases.

  1. If the return type of overloaded operator[] function is built-in type, then again a[++i]=i invokes undefined-behavior or the result is unspecified.
  2. But if the return type of overloaded operator[] function is user-defined type, then the behavior of a[++i] = i is well-defined (as far as I understand), since in this case a[++i]=i is equivalent to writing a.operator[](++i).operator=(i); which is same as, a[++i].operator=(i);. That is, assignment operator= gets invoked on the returned object of a[++i], which seems be very well-defined, since by the time a[++i] returns, ++i have already been evaluated, and then the returned object calls operator= function passing the updated value of i 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, and operator[] 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. :-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文