未定义的行为和序列点重新加载
请考虑以下主题的续集:
前一期
未定义的行为和顺序点
让我们重温这个有趣而复杂的表达方式(斜体短语取自上述主题* smile *):
i += ++i;
我们说这调用了未定义的行为。 我假设说这个时,我们隐含地认为这种类型的i
是内置类型之一。
如果i
的类型是用户定义类型会怎么样? 说它的类型是在这篇文章后面定义的Index
(见下文)。 它会不会调用未定义的行为?
如果是,为什么? 它不等同于编写i.operator+=(i.operator++());
甚至在语法上更简单i.add(i.inc());
? 或者,他们是否也调用未定义行为?
如果不是,为什么不呢? 毕竟,对象i
被连续序列点之间修改了两次 。 请回想一下经验法则:一个表达式只能在连续的“序列点之间修改一个对象的值,如果i += ++i
是一个表达式,那么它必须调用未定义的行为,如果是,那么它的等价物i.operator+=(i.operator++());
和i.add(i.inc());
还必须调用undefined-行为,这似乎是不真实的(据我所知)
或者, 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;
}
};
它看起来像代码
i.operator+=(i.operator ++());
在顺序点方面工作得很好。 C ++ ISO标准的第1.9.17节讲述了关于顺序点和功能评估的内容:
当调用一个函数时(不管函数是否内联),在函数体中执行任何表达式或语句之前,所有函数参数(如果有)的求值之后都会有一个序列点。 在复制返回值之后和执行函数外部的任何表达式之前,还有一个顺序点。
例如,这将表明i.operator ++()
作为operator +=
的参数在评估之后有一个序列点。 简而言之,因为重载操作符是函数,所以正常的顺序规则适用。
顺便提一下,很好的问题! 我真的很喜欢你如何强迫我理解我已经认为我知道的一种语言的细微差别(并认为我认为我知道)。 :-)
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 );
正如其他人所说的那样,你的i += ++i
例子与用户定义的类型一起工作,因为你在调用函数,而函数包含顺序点。
另一方面,假设a
是您的基本数组类型,或者甚至是用户定义的, a[++i] = i
并不是那么幸运。 你在这里得到的问题是,我们不知道包含i
的表达式的哪一部分首先被评估。 它可能是++i
被评估,传递给operator[]
(或原始版本),以便在那里检索对象,然后i
的值传递给那个(这是我增加后)。 另一方面,也许后面的一方首先被评估,存储用于以后的分配,然后评估++i
部分。