用C ++ 11,是不是写了f(x ++),g(x ++)?
我正在阅读这个问题:
未定义的行为和顺序点
特别是C ++ 11的答案 ,并且我理解评估“sorting”的想法。 但是 – 当我写的时候是否有足够的顺序:
f(x++), g(x++);
?
也就是说,我保证f()
得到x
的原始值, g()
得到一个一次增加的x
?
nitpickers注意事项:
- 假设
operator++()
已经定义了行为(即使我们已经覆盖它),f()
和g()
,没有任何exception会被抛出,等等 – 这个问题不是这个问题。 - 假设
operator,()
没有被重载。
不,这不是未定义的行为。
根据这个评估顺序和顺序参考 ,逗号的左边在右边之前被充分评估(见规则 9):
9)内置逗号运算符的第一个(左)参数的每个值计算和副作用在第二个(右)参数的每个值计算和副作用之前sorting。
这意味着像f(x++), g(x++)
这样的expression式是不确定的。
请注意,这仅适用于内置的逗号运算符。
引用C ++ 11(n3337) [expr.comma / 1] :
用逗号分隔的一对expression式从左到右进行求值; 左expression式是一个丢弃值expression式(Clause [expr])。 与左expression式相关的每个值计算和副作用在与右expression式相关联的每个值计算和副作用之前被sorting。
我把“每一个”表示为“每一个” 1 。 第二个x++
的评估在f
完成和f
返回的调用序列之前不会发生。 2
1析构函数调用不与子expression式相关联,只与完整的expression式有关。 所以你会看到那些以相反的顺序执行到完整expression式结尾的临时对象创build。
2本段仅适用于作为操作员使用的逗号。 当逗号具有特殊含义时(例如指定函数调用参数序列时),这不适用。
这取决于。
首先,我们假设x++
本身不会调用未定义的行为。 考虑签名溢出,增加一个过去的结束指针,或后缀增量运算符可能是用户定义的)。
此外,让我们假设用它们的参数调用f()
和g()
并销毁临时对象不会调用未定义的行为。
这是相当多的假设,但如果他们被打破了,答案是微不足道的。
现在,如果逗号是内置的逗号运算符,括号初始化列表中的逗号或者mem-initializer-list中的逗号,则左侧和右侧将在彼此之前或之后进行sorting(以及你知道哪个),所以不要干涉,使行为明确。
struct X { int f, g; explicit X(int x) : f(x++), g(x++) {} }; // Demonstrate that the order depends on member-order, not initializer-order: struct Y { int g, f; explicit Y(int x) : f(x++), g(x++) {} }; int y[] = { f(x++), g(x++) };
否则,如果x++
为后缀增量调用用户定义的运算符重载,则会对x++
的两个实例进行不确定的sorting,从而导致未指定的行为。
std::list<int> list{1,2,3,4,5,6,7}; auto x = begin(list); using T = decltype(x); void h(T, T); h(f(x++), g(x++)); struct X { X(T, T) {} } X(f(x++), g(x++));
在最后的情况下,由于x
的两个后缀增量是不确定的,所以你会得到完全未定义的行为。
int x = 0; void h(int, int); h(f(x++), g(x++)); struct X { X(int, int) {} } X(f(x++), g(x++));