调用参数排列不变函数 f(i++, i++)

Calling an argument-permutation-invariant function f(i++, i++)

本文关键字:i++ 函数 参数 排列 调用      更新时间:2023-10-16

>假设我有一个函数foo(x, y, z)它是不变的w.r.t.其参数的所有排列。我还有一个迭代器it,这样迭代器itit + 1it + 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)吗?

(可能,这个问题是基于意见的。但我忍不住问了。

你是对的,在 C++17

foo(*it++, *it++, *it++);

不是未定义的行为。 正如您的报价所述,如 [expr.call]/8 中所述

后缀表达式在表达式列表中的每个表达式和任何默认参数之前排序。参数的初始化,包括每个关联的值计算和副作用,相对于任何其他参数的初始化都是不确定的。

每个增量都是按顺序排列的,因此您不会有多个未排序的写入。 函数参数的计算顺序在 C++17 中仍未指定,因此这意味着您只有未指定的行为(您无法知道将哪个元素传递给每个参数(。

只要你的函数不关心这个,那么你就没问题。 如果参数的顺序很重要,那么您将不得不使用第二个版本。


这一切都说我更喜欢使用

foo(*it, *(it + 1), *(it + 2));

即使顺序无关紧要。 它使代码向后兼容,恕我直言,它更容易推理。 我宁愿在 for 循环的增量部分看到一个it += 3,然后在函数调用中看到多个增量,而在循环的增量部分没有增量。