在 C++ 语言中,对于许多情形,标准并未规定表达式中子表达式的求值顺序。这可以使得编译器更好地优化代码,但是在一些场合会产生难以调试的 bug。
如果 read
是一个自定义的“快速”读入函数,则下列读入并构造一个 pair<int, int>
的代码片段可能产生难以预料的结果:
pair<int, int> p(read(), read());
由于标准并未规定调用函数时对参数求值的顺序,编译器可能决定对后一个参数先进行求值。此时,如果读入
1 2
则 p
可能是 {2, 1}
。
注意函数参数未必从后向前求值,即 p
未必是 {2, 1}
,也可能是 {1, 2}
。某些过时的 C/C++ 教材中对此存在错误描述。一般地,我们只能说求值顺序是不确定的,具体的顺序可能是任意一种全排列。
没有很好的检查方法,因为未指定行为并非编程错误,它只是可能导致编程错误,故编译器很难给出有用的提示。只能人工审查代码。
对于示例,最简单的修复方法是改为使用初始化列表进行构造
pair<int, int> p = {read(), read()};
这是由于语言标准严格规定初始化列表必须从左向右求值。一般地,对于 POD 数据类型,建议总是使用初始化列表构造,这样既可以避免此类问题,又可以少写一个构造函数。