В описании к named requirement InputIterator в таблице приведено в пример выражение ((void)i++), на которое я никогда ранее не натыкался. В чём смысл каста к void и почему это вообще валидно?
- 1,945
1 Answers
Канонический оператор постинкремента в выражении i++ выполняет следующие действия:
- запомнить текущее значение
i - увеличить
iна "единицу" (с учётом типа переменной) - вернуть прежнее значение
iдо увеличения
Причём возврат прежнего значения - это именно фишка пост-версии оператора. Если нет необходимости получать старое значение, а просто "шагнуть вперёд", то более правильным вариантом будет использование преинкремента. Соответственно, если результат работы постинкремента никак не использован в коде, то компилятор логично может предположить, что в коде допущена ошибка и выдать соответствующее предупреждение. Явно такая необходимость использования возвращаемого значения может быть указана с помощью атрибута [[nodiscard]]. А для подавления подобных предупреждений как раз и используется приведение к void. Т.е. этим мы сообщаем компилятору, что несмотря на использование постинкремента, возвращаемое значение нас не интересует.
В упомянутой вами таблице добавление (void) позволяет показать связь между выражениями i++ и ++i. То есть, если возвращаемые значения игнорируются, их действия эквиваленты, иначе они, конечно же, будут отличаться.
- 28,987
- 13
- 60
- 119
-
for (int idx = 0; idx < size; ++idx) {}иfor (int idx = 0; idx < size; idx++) {}фактически ничем отличаться не будут, т.к. ненаблюдаемое поведение, и, соответственно, оптимизация. – megorit Oct 22 '21 at 16:55 -
@megorit для
intне будут, а для какого-нибудь класса - будут. Хотя бы даже для итераторов. – αλεχολυτ Oct 22 '21 at 16:57 -
voidчтобы линтер или компилятор не ругался на неиспользованное значение. Напримерprintfбез(void)заставит линтер ругаться ("надо проверить что всё что напечатано - напечатано!"). А нам не хочется следовать этим строгостям:(void)printf(...). – Stanislav Volodarskiy Oct 09 '21 at 13:48T. Для плюсов это привычнее выглядит) – megorit Oct 09 '21 at 14:02voidозначает вычислить выражение, а результирующее значение просто отбросить, expr.static.cast/6. Оба инкремента++iиi++делают две вещи: 1) возвращают некоторое значение, 2) изменяют внутреннее состояние итератора. То что выражение(void)i++эквивалентно выражению(void)++i— это такой изощрённый способ сказать, что они оба одинаковым образом изменяют внутреннее состояние итератора, но возвращаемые значения могут отличаться. – wololo Oct 09 '21 at 14:42лучше бы написали просто TЕсли написать, что 1)++iвозвращаетIt&, 2)i++возвращаетT, 3)i++эквивалентен++i, то тогда выходит, чтоTдолжен совпадать сIt&, а это не так. – wololo Oct 09 '21 at 14:45voidможет применяться для подавления предупреждений компилятора при использовании атрибута nodiscard, а также для подавления вызова перегруженной версии оператора запятая вместо встроенной (см.: Why does std::transform and similar cast the 'for' loop increment to (void)?). – wololo Oct 09 '21 at 14:55*++iterточно будет отличаться от*iter++. Спасибо за ответы. – megorit Oct 09 '21 at 14:59(void)говорит "возвращайте что хотите, меня интересует только побочный эффект". Тут свободы больше. – Stanislav Volodarskiy Oct 09 '21 at 17:10