x++
的评估不能在调用序列到 f
完成并返回之前发生。2
1 析构函数调用与子表达式无关,仅与完整表达式有关。因此,在完整表达式结束时,您将看到这些以相反的顺序执行以临时对象的创建为结尾。
2 此段落仅适用于逗号用作运算符的情况。当逗号具有特殊含义时(例如指定函数调用参数序列),则不适用此规则。
根据此评估顺序和排序参考,逗号左侧在右侧之前完全评估 (见规则 9):
9) 内置逗号运算符的第一个 (左侧) 实参的每个值计算和副作用都在第二个 (右侧) 实参的每个值计算和副作用之前排序。
这意味着诸如 f(x++), g(x++)
的表达式不是未定义的。
请注意,这仅适用于内置逗号运算符。
首先,假设x++
本身不会引起未定义行为。请考虑有符号数溢出、增量一个超过末尾指针或者后缀递增运算符可能是用户定义的情况。
此外,假设使用参数调用f()
和g()
并销毁这些临时变量不会引起未定义行为。
这是相当多的假设,但如果它们被破坏了,答案就很显然了。
现在,如果逗号是内置逗号运算符、花括号初始化列表中的逗号或者成员初始化列表中的逗号,则左侧和右侧都会被顺序执行(您知道哪个在前面或后面),因此不会产生干扰,使行为具有明确定义。
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++
实例的顺序,从而导致未指定的行为。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++));
,
运算符是可行的,如果,
运算符是用户定义的,则不可行。参见:http://en.cppreference.com/w/cpp/language/operator_other#Built-in_comma_operator(这在C++17中有所改变)。 - Richard Crittenabort();
,但那并没有太多帮助,对吗? - einpoklumoperator,( f(x++), g(x++) );
不是未定义行为,如果假设x==0
,则会先调用f(0)
再调用g(1)
,或者先调用g(0)
再调用f(1)
;执行完后,x
的值将变成2。之前曾经提出过函数调用应该采用从左到右的严格顺序计算,但据我所知目前还没有实现。 - M.M