序列点和方法链接

以下表达式通常用于演示未定义的未指定行为:
f() + g()
如果
f()
g()
都对某些共享对象有副作用,那么行为未定义未指定,因为执行顺序未知。
f()
可以在
g()
之前评估,反之亦然。 现在我想知道当你在一个对象上链接成员函数时会发生什么。假设我有一个类的实例,实例名为
obj
,它有两个成员函数,
foo()
bar()
都修改了对象。这些函数的执行顺序不是可交换的。将一个调用到另一个之前的效果与通过相反方式调用它们的效果不同。两种方法都返回对
*this
的引用,以便它们可以像这样链接:
obj.foo().bar()
但这是不明确的行为吗?我在标准中找不到任何东西(不可否认只是扫描),区分了这个表达式和我在帖子顶部给出的表达。两个函数调用都是full-expression的子表达式,因此它们的执行顺序是未指定的。但肯定必须首先评估
foo()
,以便
bar()
知道要修改哪个对象。 也许我错过了一些明显的东西,但我无法看到序列点的创建位置。     
已邀请:
f() + g()
这里的行为未指定(未定义),因为未指定评估每个操作数的顺序(即,调用每个函数)。
 obj.foo().bar();
这在C ++中是明确定义的。 C ++ ISO标准的相关章节§1.9.17中,   调用函数时(无论是否为   不是函数是内联的),有   评估后的序列点   所有函数参数(如果有)   这是在执行之前发生的   中的任何表达或陈述   功能体。还有一个   复制后的序列点   返回值和之前   在外面执行任何表达式   功能。 在这些主题中已经详细讨论了类似的案例: 重新加载未定义的行为和序列点 这段代码是否定义明确?     
如果f()和g()都对某些共享对象有副作用,则行为未定义,因为执行顺序未知。 这不是真的。函数调用不进行交错,在进入函数之前和离开函数之前有一个序列点。
g
中与side15ѭ中的副作用相关的所有副作用至少由一个序列点分隔。行为未定义。 因此,函数
f
g
的执行顺序没有确定,但是一旦执行了一个函数,只执行该函数的评估,而另一个函数“必须等待”。不同的可观察结果是可能的,但这并不意味着发生了未定义的行为。   现在我想知道当你在一个对象上链接成员函数时会发生什么。 如果你有
obj.foo().bar()
那么你需要首先评估
obj.foo()
以了解你称之为功能object20ѭ的对象,这意味着你必须等待
obj.foo()
返回并产生一个值。然而,这并不一定意味着评估
obj.foo()
引起的所有副作用都已完成。在评估表达式后,您需要一个序列点来将这些副作用视为完整。因为在从
obj.foo()
返回之前以及在调用
bar()
之前存在一个序列点,所以你实际上已经确定了通过分别在
foo
bar
中计算表达式来执行副作用的顺序。 为了解释一下,在你的例子中,在
bar
之前调用
foo
的原因与在下面调用函数
f
之前首先递增
i++
的原因相同。
int i = 0;
void f() {
  std::cout << i << std::endl;
}

typedef void (*fptype)();
fptype fs[] = { f };

int main() {
  fs[i++]();
}
这里要问的问题是:这个程序是打印
0
1
还是其行为未定义或未指定?答案是,因为在函数调用之前必须首先计算表达式
fs[i++]
,并且在输入
f
之前有一个序列点,
f
里面的
i
的值是
1
。 我认为你不需要将隐式对象参数的范围扩展到序列点来解释你的情况,你当然不能扩展它来解释这种情况(我希望是定义的行为)。 C ++ 0x草案(不再有序列点)有一个更明确的措辞(强调我的)   当调用函数(函数是否为内联函数)时,与任何参数表达式相关联的每个值计算和副作用,或者使用指定被调用函数的后缀表达式,都会在执行每个表达式或语句之前对其进行排序。叫功能。     
Foo()将在bar()之前执行。我们必须首先确定Foo(),否则我们不知道bar()应该采取什么行动(我们甚至不知道什么类型的类bar()属于。如果你认为,你可能会更直观如果foo返回一个新的obj实例,而不是这个,我们必须这样做。或者如果foo返回一个完全不同的类的实例,它也定义了一个bar()方法。 你可以自己测试一下foo和bar中的断点,看看哪个被击中。     

要回复问题请先登录注册