调用参数 - 置换 - 不变函数f(i ++,i ++)

Evg*_*Evg 2 c++ c++17

假设我的函数foo(x, y, z)在其参数的所有排列中都是不变的.我也有一个迭代it,使得迭代器it,it + 1并且it + 2可以解除引用.

写得好吗

... = foo(*it++, *it++, *it++);  // (1)
Run Code Online (Sandbox Code Playgroud)

代替

... = foo(*it, *(it + 1), *(it + 2));  // (2)
Run Code Online (Sandbox Code Playgroud)

据我所知,技术上它是正确的,因为C++ 17由于(引用cppreference.com并考虑到它it可以是一个原始指针)

15)在函数调用中,关于任何其他参数的值计算和副作用,每个参数的初始化的值计算和副作用是不确定地排序的.

函数参数的评估顺序没有定义,但对于foo()顺序无关紧要.

但这是一种可接受的编码风格吗?一方面,它(1)非常对称,暗示foo具有这样的不变性,(2)看起来有些难看.另一方面,(1)立即提出有关其正确性的问题 - 阅读代码的人应该检查描述或定义foo以验证呼叫的正确性.

如果身体foo()很小并且函数定义中的不变性很明显,你会接受(1)吗?

(可能这个问题是基于意见的.但我不禁要问它.)

Nat*_*ica 6

你是正确的在C++ 17

foo(*it++, *it++, *it++);
Run Code Online (Sandbox Code Playgroud)

不是未定义的行为.正如您的引用所述,并且如[expr.call]/8中所述

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

每个增量都按顺序进行排序,因此您没有多次无序写入.函数参数的评估顺序在C++ 17中仍未指定,因此这意味着您只有未指定的行为(您无法知道哪个元素传递给每个参数).

只要你的功能不关心这个,那你就没事了.如果参数的顺序很重要,那么你将不得不使用你的第二个版本.


这一切都说我更喜欢使用

foo(*it, *(it + 1), *(it + 2));
Run Code Online (Sandbox Code Playgroud)

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