ein*_*ica 42 c++ expression undefined-behavior c++11
我正在读这个问题:
并且,特别是C++ 11的答案,我理解评估的"排序"的想法.但是 - 写作时是否有足够的顺序:
f(x++), g(x++); ?
也就是说,我保证f()获得原始值x并g()获得一次递增x?
挑剔的注意事项:
operator++()已定义的行为(即使我们已经重写它),这样做f()和g(),没有异常将被抛出,等-这个问题是不是有关.operator,()没有超载.Sto*_*ica 49
不,行为已定义.引用C++ 11(n3337)[expr.comma/1]:
用逗号分隔的一对表达式从左到右进行评估; 左表达式是一个废弃值表达式(Clause [expr]). 在与右表达式相关联的每个值计算和副作用之前,对与左表达式相关联的每个值计算和副作用进行排序.
我把"每一个"都称为"每一个" 1.x++在呼叫序列f完成并f返回之前,不能进行第二次评估.2
1析构函数调用与子表达式无关,仅与完整表达式相关联.因此,您将看到在完整表达式结束时以相反顺序执行的临时对象创建.
2本段仅适用于作为运营商使用的逗号.当逗号具有特殊含义时(例如在指定函数调用参数序列时),这不适用.
Som*_*ude 23
根据此评估顺序和排序参考,逗号的左侧在右侧之前完全评估(参见规则 9):
9)内置逗号运算符的第一个(左)参数的每个值计算和副作用在每个值计算和第二个(右)参数的副作用之前排序.
这意味着像的表达f(x++), g(x++)是不未定义.
请注意,这仅对内置逗号运算符有效.
Ded*_*tor 12
首先,让我们假设它x++本身不会调用未定义的行为.考虑有符号溢出,递增过去的结束指针,或者postfix-increment-operator可能是用户定义的).
此外,让我们假设调用f()和g()使用他们的参数并销毁临时对象不会调用未定义的行为.
这是相当多的假设,但如果它们被打破,答案是微不足道的.
现在,如果逗号是内置的逗号操作符,逗号的支撑,初始化列表,或在MEM-初始化列表中的逗号,左侧和右侧分别之前或之后彼此测序(和你知道哪个),所以不要干涉,使行为定义明确.
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++) };
Run Code Online (Sandbox Code Playgroud)
否则,如果x++为postfix-increment调用用户定义的operator-overload,则会对两个实例进行不确定的排序,x++从而对未指定的行为进行排序.
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++));
Run Code Online (Sandbox Code Playgroud)
在最后一种情况下,你得到了完整的未定义行为,因为两个后缀增量x是未经测序的.
int x = 0;
void h(int, int);
h(f(x++), g(x++));
struct X {
X(int, int) {}
}
X(f(x++), g(x++));
Run Code Online (Sandbox Code Playgroud)