用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++));