假设我有一个不变的函数foo(x, y, z)
。其参数的所有排列。我还拥有一个迭代器it
,因此可以取消引用迭代器it
,it + 1
和it + 2
。
写好吗
... = foo(*it++, *it++, *it++); // (1)
代替
... = foo(*it, *(it + 1), *(it + 2)); // (2)
?
据我所知,从技术上讲,这是正确的,因为C ++ 17是由于(引用cppreference.com并考虑到it
可能是原始指针)
15)在函数调用中,相对于任何其他参数的值计算和副作用,每个参数初始化的值计算和副作用都不确定地排序。
函数参数的求值顺序未定义,但是对于foo()
而言,顺序无关紧要。
但这是可接受的编码样式吗?一方面,(1)
具有很好的对称性,并暗示foo
具有这样的不变性,而(2)
看起来有些丑陋。另一方面,(1)
立即提出有关其正确性的问题-读取代码的人应检查foo
的描述或定义以验证呼叫的正确性。
如果foo()
的主体很小,并且从函数定义中可以明显看出不变性,您会接受(1)
吗?
(这个问题可能是基于观点的。但是我不禁要问。)
答案 0 :(得分:6)
您是正确的,在C ++ 17中
foo(*it++, *it++, *it++);
不是未定义的行为。如您的报价所述,并按[expr.call]/8
中所述postfix-expression在expression-list中的每个表达式和任何默认参数之前进行排序。参数的初始化(包括每个相关的值计算和副作用)相对于任何其他参数的不确定地排序。
每个增量都是有序的,因此您不会有多个未排序的写操作。在C ++ 17中,函数参数的求值顺序仍未指定,这意味着您只是具有未指定的行为(您不知道将哪个元素传递给每个参数)。
只要您的函数对此不关心,就可以了。如果参数的顺序很重要,则必须使用第二个版本。
所有人都说我更喜欢使用
foo(*it, *(it + 1), *(it + 2));
即使顺序无关紧要。它使代码向后兼容,恕我直言,因此更容易推理。我宁愿在for循环的增量部分中看到一个it += 3
,然后在函数调用中看到多个增量,而在循环的增量部分中看到没有增量。