论坛首页 编程语言技术论坛

Sequence Point (呜呜,综合技术木有人看,于是转过来)

浏览 6871 次
精华帖 (0) :: 良好帖 (6) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-12-15   最后修改:2009-12-15
dicom 写道
mikeandmore 写道
Solstice 写道
f(g(), h()) 中,g() 和 h() 的求值顺序也是未指明(unspecified)的,这点需要注意。

嗯查了一下,的确是没有规定的。

我的但忽然觉得我的那个解决方案还是对的。。。。-,-因为不是换成了,operator了。。。。。刚才放出来一写才发现
const test& operator=(const test& rhs)....... 明明是重载成了函数。。。。-,-不是成了参数。。。汗。。。

于是编辑了。。。


利用operator重载去解决这种问题,非常隐晦,难以理解,其实就用简单的语法语句,显式的将执行顺序定义清楚(如果执行顺序相关),简单而清晰:
int i = g();
int j = h();
f(i, j);

谁能理解 f(i++, ++i, ++i, i++)这种垃圾代码?

你这是极端现象,这谁都知道难懂,也没有人会去写这种代码。(而且我怀疑这种代码应该是没有workaround能让他正确执行的)

我的意思是,很多东西不是保证顺序的。而C++很多东西是隐饰的。这会造成我的问题,但是问题的原因并不是可读性

谁能保证左边右边的side effect发生的不冲突?就像我最开始的例子
vec[current] = build_tree(vec, a, b)如果vec是数组,一点问题都没有,我想,如果vec是数组,大部分程序员还是可以接受这种代码的吧。
这种例子还有很多
比如
node->left = build_sub_tree();

你很难保证build_sub_tree就不会对node->left操作吧。(当然我承认这是坏设计,但绝对不是坏的可读性)
0 请登录后投票
   发表时间:2009-12-15  
mikeandmore 写道
mikeandmore 写道
Solstice 写道
mikeandmore 写道
Solstice 写道
f(g(), h()) 中,g() 和 h() 的求值顺序也是未指明(unspecified)的,这点需要注意。

嗯查了一下,的确是没有规定的。

我的但忽然觉得我的那个解决方案还是对的。。。。-,-因为不是换成了,operator了。。。。。刚才放出来一写才发现
const test& operator=(const test& rhs)....... 明明是重载成了函数。。。。-,-不是成了参数。。。汗。。。

于是编辑了。。。

重载 operator= 不是办法,因为它其实有两个参数,求值顺序仍然是 unspecified.

你是说运行时的时候有两个参数?
这个倒是真的,但是标准似乎规定的是function call operator是sequence。
也就是说a.f(b)的parse顺序是b->()->a (inside f),所以()应该是在a,b之间的,所以a.f和b的顺序是确定的。

a sample
#include <iostream>

class test {
public:
    // const test& operator=(const test& rhs) {
    //     return (*this);
    // }
};

class test_run {
    test data;
public:
    test& operator[](int idx) {
        std::cout << "[]" << std::endl;
        return data;
    }
    const test& func() {
        std::cout << "func" << std::endl;
        return data;
    }
};

int main(int argc, char *argv[])
{
    test_run r;
    r[0] = r.func();
    return 0;
}


mike@mike-laptop% ./a.out    
[]
func


uncomment以后
mike@mike-laptop% ./a.out    
func
[]


嗯,由于我的编译器(g++4.4)的sequence point ambigious比较严重,所以还是能部分说明问题的

去掉注释之后,用 Intel ICC 11.0 编译,提示
remark #981: operands are evaluated in unspecified order
r[0] = r.func();
     ^
所以我说重载 operator= 根本不是解决办法,它碰巧调整了顺序而已,而且这种调整是靠不住的,会随编译器变化而变化。
0 请登录后投票
   发表时间:2009-12-16   最后修改:2009-12-16
Solstice 写道

去掉注释之后,用 Intel ICC 11.0 编译,提示
remark #981: operands are evaluated in unspecified order
r[0] = r.func();
     ^
所以我说重载 operator= 根本不是解决办法,它碰巧调整了顺序而已,而且这种调整是靠不住的,会随编译器变化而变化。

看上去像个icc的issue
http://software.intel.com/en-us/articles/cdiag981/


http://software.intel.com/en-us/forums/intel-c-compiler/topic/46883
2楼那种情况也是有问题的。而显然那是个及联函数调用,标准里明显规定过的顺序的。


引用

ICC does recognize and process the "pure" attribute, much like GCC. Unfortunately, it matters only to the optimizer, and specifically, it doesn't affect the emission of #981. In C++, functions without side effects tend to be so common that our compiler developers recommend disabling #981 -- the noise-to-signal ratio is typically just too high. But for C code, #981 is more likely to be useful.

Hope the above helps. Let me know if you need any more info.

http://software.intel.com/en-us/forums/intel-c-compiler/topic/62309/

于是,这应该和重载=后,但是等于没有发生side effect有关。
你试试看

#include <iostream>

class test {
    int t;
public:
    const test& operator=(const test& rhs) {
        t = rhs.t;
        return (*this);
    }
};

class test_run {
    test data;
public:
    test& operator[](int idx) {
        std::cout << "[]" << std::endl;
        return data;
    }
    const test& func() {
        std::cout << "func" << std::endl;
        return data;
    }
};

int main(int argc, char *argv[])
{
    test_run r;
    r[0] = r.func();
    return 0;
}

这样子呢?
0 请登录后投票
   发表时间:2009-12-16   最后修改:2009-12-16
看了更多的sample,icc不靠谱。似乎intel那群人是无视所有sequence point的。

你可以查查C++标准,里面应该是说过一个operator被重载后一定是一个function call了,而function call是sequence point的。

-----------------------------------------------------------------------------------------
抛开我们的话题。这样的东西。
cout << vec.size() << endl;
根据标准规定,这种代码是绝对没有问题的。

但是唯独icc给出warning。所以icc compiler developer建议suppress掉这个warning了。
0 请登录后投票
   发表时间:2009-12-16  
mikeandmore 写道
看了更多的sample,icc不靠谱。似乎intel那群人是无视所有sequence point的。

你可以查查C++标准,里面应该是说过一个operator被重载后一定是一个function call了,而function call是sequence point的。

-----------------------------------------------------------------------------------------
抛开我们的话题。这样的东西。
cout << vec.size() << endl;
根据标准规定,这种代码是绝对没有问题的。

但是唯独icc给出warning。所以icc compiler developer建议suppress掉这个warning了。

函数调用是序列点,这没错。但是成员函数有隐藏参数(this 指针),你定义的 operator= 相当于有两个参数,而这两个参数的求值顺序是未定义的。
0 请登录后投票
   发表时间:2009-12-16  
Solstice 写道
mikeandmore 写道
看了更多的sample,icc不靠谱。似乎intel那群人是无视所有sequence point的。

你可以查查C++标准,里面应该是说过一个operator被重载后一定是一个function call了,而function call是sequence point的。

-----------------------------------------------------------------------------------------
抛开我们的话题。这样的东西。
cout << vec.size() << endl;
根据标准规定,这种代码是绝对没有问题的。

但是唯独icc给出warning。所以icc compiler developer建议suppress掉这个warning了。

函数调用是序列点,这没错。但是成员函数有隐藏参数(this 指针),你定义的 operator= 相当于有两个参数,而这两个参数的求值顺序是未定义的。


这个一般倒是不会有什么问题,this 指针求值是先是后很少会造成麻烦。
不过大方向上我支持你的观点,用重载 operator= 来避开 sequence point 问题不是一个合理的方案。
0 请登录后投票
   发表时间:2009-12-16  
Solstice 写道
mikeandmore 写道
看了更多的sample,icc不靠谱。似乎intel那群人是无视所有sequence point的。

你可以查查C++标准,里面应该是说过一个operator被重载后一定是一个function call了,而function call是sequence point的。

-----------------------------------------------------------------------------------------
抛开我们的话题。这样的东西。
cout << vec.size() << endl;
根据标准规定,这种代码是绝对没有问题的。

但是唯独icc给出warning。所以icc compiler developer建议suppress掉这个warning了。

函数调用是序列点,这没错。但是成员函数有隐藏参数(this 指针),你定义的 operator= 相当于有两个参数,而这两个参数的求值顺序是未定义的。


搞定,查了一下C++的标准,你是对的,这样解决的确不彻底,类似于编译器的workaround了。

但是原因不是this是隐藏参数。C++里的method的this只能算parameter,不能算argument.
换句话说this的来历要比argument复杂的多,因为引入了postfix-expression了。

不过,C++标准专门用了一段来规定,postfix-expression和argument在()之前被sequence的次序是不确定的。
是5.2.2里面的第8段。

估计我这样做可以是有历史原因的。因为历来C++的sequence point就是争论的焦点,所以标准里面也改了好多次,这个5.2.2p8是07年加上去的。原因是5.3的意思似乎有一点认为postfix-expression是在argument之前执行,并且绑定到this parameter上的。但是说的含糊其辞,1.9里面虽然说argument是乱序的,但是只字没提postfix-expression

于是继续编辑。 
0 请登录后投票
   发表时间:2009-12-16  
Elminster 写道
Solstice 写道
mikeandmore 写道
看了更多的sample,icc不靠谱。似乎intel那群人是无视所有sequence point的。

你可以查查C++标准,里面应该是说过一个operator被重载后一定是一个function call了,而function call是sequence point的。

-----------------------------------------------------------------------------------------
抛开我们的话题。这样的东西。
cout << vec.size() << endl;
根据标准规定,这种代码是绝对没有问题的。

但是唯独icc给出warning。所以icc compiler developer建议suppress掉这个warning了。

函数调用是序列点,这没错。但是成员函数有隐藏参数(this 指针),你定义的 operator= 相当于有两个参数,而这两个参数的求值顺序是未定义的。


这个一般倒是不会有什么问题,this 指针求值是先是后很少会造成麻烦。
不过大方向上我支持你的观点,用重载 operator= 来避开 sequence point 问题不是一个合理的方案。

见ls的恢复-w-

新标准里面专门说过了,this和argument是乱序的。

PS 其实general 的sequence point问题肯定无法避免的,我只是想找仅仅对于赋值的方案。
0 请登录后投票
   发表时间:2009-12-16  
mikeandmore 写道
Elminster 写道
Solstice 写道
mikeandmore 写道
看了更多的sample,icc不靠谱。似乎intel那群人是无视所有sequence point的。

你可以查查C++标准,里面应该是说过一个operator被重载后一定是一个function call了,而function call是sequence point的。

-----------------------------------------------------------------------------------------
抛开我们的话题。这样的东西。
cout << vec.size() << endl;
根据标准规定,这种代码是绝对没有问题的。

但是唯独icc给出warning。所以icc compiler developer建议suppress掉这个warning了。

函数调用是序列点,这没错。但是成员函数有隐藏参数(this 指针),你定义的 operator= 相当于有两个参数,而这两个参数的求值顺序是未定义的。


这个一般倒是不会有什么问题,this 指针求值是先是后很少会造成麻烦。
不过大方向上我支持你的观点,用重载 operator= 来避开 sequence point 问题不是一个合理的方案。

见ls的恢复-w-

新标准里面专门说过了,this和argument是乱序的。

PS 其实general 的sequence point问题肯定无法避免的,我只是想找仅仅对于赋值的方案。


我知道 this 和 argument 是乱序的,上一贴的重点是对 this 指针求值的先后次序变化一般不会给你带来麻烦。
另外即使是仅仅对于赋值,我也不赞同这个利用重载的办法,复杂而且不直观,你会需要一些文档和注释来解释你为啥要引入这个看起来毫无必要的重载,以及 —— 在你的例子里 —— 为了引入这个重载而进一步引入的一个类型。设法打断那个赋值语句把副作用分散开,应该是一个更好的方案。
0 请登录后投票
   发表时间:2009-12-17  
Elminster 写道
mikeandmore 写道
Elminster 写道
Solstice 写道
mikeandmore 写道
看了更多的sample,icc不靠谱。似乎intel那群人是无视所有sequence point的。

你可以查查C++标准,里面应该是说过一个operator被重载后一定是一个function call了,而function call是sequence point的。

-----------------------------------------------------------------------------------------
抛开我们的话题。这样的东西。
cout << vec.size() << endl;
根据标准规定,这种代码是绝对没有问题的。

但是唯独icc给出warning。所以icc compiler developer建议suppress掉这个warning了。

函数调用是序列点,这没错。但是成员函数有隐藏参数(this 指针),你定义的 operator= 相当于有两个参数,而这两个参数的求值顺序是未定义的。


这个一般倒是不会有什么问题,this 指针求值是先是后很少会造成麻烦。
不过大方向上我支持你的观点,用重载 operator= 来避开 sequence point 问题不是一个合理的方案。

见ls的恢复-w-

新标准里面专门说过了,this和argument是乱序的。

PS 其实general 的sequence point问题肯定无法避免的,我只是想找仅仅对于赋值的方案。


我知道 this 和 argument 是乱序的,上一贴的重点是对 this 指针求值的先后次序变化一般不会给你带来麻烦。
另外即使是仅仅对于赋值,我也不赞同这个利用重载的办法,复杂而且不直观,你会需要一些文档和注释来解释你为啥要引入这个看起来毫无必要的重载,以及 —— 在你的例子里 —— 为了引入这个重载而进一步引入的一个类型。设法打断那个赋值语句把副作用分散开,应该是一个更好的方案。

这是一个递归的结论-w-,this的求值顺序要看postfix-expression的,postfix-express当然也会发生unsequence的求值结果。-w-
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics