这段代码摘自对这里的讨论。
someInstance.Fun(++k).Gun(10).Sun(k).Tun();
这段代码定义清楚吗?在Sun()中,++k
在k
之前评估过吗?
如果k
是用户定义的类型,而不是内置类型呢?上述函数调用order的方式与此不同:
eat(++k);drink(10);sleep(k);
据我所知,在这两种情况下,在每个函数调用之后都存在一个序列点。如果是这样的话,为什么第一种情况也不能像第二种一样被很好地定义呢?
C++ ISO标准的第1.9.17节提到了顺序点和功能评估:
当调用一个函数(不管函数是否内联)时,在计算所有函数参数(如果有的话)之后有一个序列点(如果有的话),它发生在函数体中的任何表达式或语句执行之前。在复制返回的值之后还有一个序列点,在函数之外的任何表达式执行之前有一个。
发布于 2011-01-17 05:16:35
这取决于如何定义Sun
。以下是明确的定义:
struct A {
A &Fun(int);
A &Gun(int);
A &Sun(int&);
A &Tun();
};
void g() {
A someInstance;
int k = 0;
someInstance.Fun(++k).Gun(10).Sun(k).Tun();
}
如果将Sun
的参数类型更改为int
,则会出现未定义的情况。让我们把画一棵树的版本取一个int
。
<eval body of Fun>
|
% // pre-call sequence point
|
{ S(increment, k) } <- E(++x)
|
E(Fun(++k).Gun(10))
|
.------+-----. .-- V(k)--%--<eval body of Sun>
/ \ /
E(Fun(++k).Gun(10).Sun(k))
|
.---------+---------.
/ \
E(Fun(++k).Gun(10).Sun(k).Tun())
|
% // full-expression sequence point
可以看到,我们读到了k
(由V(k)
指定)和一个对k
的副作用(在最上面),它们不被序列点分隔:在这个表达式中,相对于其他子表达式,根本没有序列点。最底层的%
表示完整的表达式序列点。
发布于 2011-01-16 19:51:19
我认为,如果你准确地读到了这句标准的话,第一种情况就不会得到很好的界定:
当调用一个函数时(不管函数是否内联),在计算所有函数参数(如果有的话)之后会出现一个序列点,它发生在函数体中的任何表达式或语句执行之前。
这告诉我们的不是“在对函数的参数进行了计算之后可以发生的唯一事情是实际的函数调用”,而仅仅是在参数计算结束后的某个点和函数调用之前有一个序列点。
但如果你想象这样的情况:
foo(X).bar(Y)
这给我们的唯一保证是:
X
之前对foo
进行评估,并且Y
之前对bar
进行评估。但是,这样的命令仍然是可能的:
X
Y
X
与foo
调用分离的序列点)foo
Y
与bar
调用分离的序列点)bar
当然,我们也可以交换前两项,在X
之前评估X
。为什么不行?标准只要求在函数体的第一条语句之前对函数的参数进行完全评估,而上面的序列满足这一要求。
至少这是我的解释。它似乎并没有说在参数评估和函数体之间没有其他的事情发生--只是这两者被一个序列点分开。
发布于 2011-01-16 19:27:45
这是未定义的行为,因为k的值在相同的表达式中被修改和读取,没有中间的序列点。请看对这个问题的极好的长篇回答。
1.9.17的引号告诉您,所有函数参数都是在调用函数体之前计算的,但是没有提到参数与同一表达式中不同函数调用的相对顺序--不能保证"++k Fun()在Sun()中的k之前计算“。
eat(++k);drink(10);sleep(k);
因为;
是一个序列点,所以计算的顺序是明确的。
https://stackoverflow.com/questions/4709727
复制