Вызов функции-инварианта аргумента-перестановки f(i++, i++)

Предположим, у меня есть функция 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)?

(Возможно, этот вопрос основан на мнении. Но я не могу не задать его.)

1 ответ

Решение

Вы правы в том, что в C++17

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

не неопределенное поведение. Как говорится в вашей цитате и как указано в [expr.call]/8

Постфиксное выражение упорядочивается перед каждым выражением в списке выражений и любым аргументом по умолчанию. Инициализация параметра, включая каждое связанное с ним вычисление значения и побочный эффект, определяется неопределенным образом относительно последовательности любого другого параметра.

каждый шаг упорядочен, поэтому у вас нет нескольких непоследовательных записей. Порядок вычисления аргументов функции все еще не определен в C++17, так что это означает, что у вас просто неопределенное поведение (вы не можете знать, какой элемент передается каждому аргументу).

Пока ваша функция не заботится об этом, вы в порядке. Если порядок аргументов будет иметь значение, вам придется использовать вторую версию.


Это все сказал, что я предпочел бы использование

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

даже если порядок не имеет значения. Это делает код обратно совместимым, и, по-моему, его легче рассуждать. Я бы предпочел увидеть it += 3 в инкрементной части цикла for выполняется многократное увеличение в вызове функции, а в инкрементной части цикла нет приращения.

Другие вопросы по тегам