3

В описании к named requirement InputIterator в таблице приведено в пример выражение ((void)i++), на которое я никогда ранее не натыкался. В чём смысл каста к void и почему это вообще валидно?

megorit
  • 1,945
  • 1
    В документации это выражение означает что должен быть определён постинкремент с любым возвращаемым типом. – Stanislav Volodarskiy Oct 09 '21 at 13:46
  • 1
    В коде иногда выражение приводят к void чтобы линтер или компилятор не ругался на неиспользованное значение. Например printf без (void) заставит линтер ругаться ("надо проверить что всё что напечатано - напечатано!"). А нам не хочется следовать этим строгостям: (void)printf(...). – Stanislav Volodarskiy Oct 09 '21 at 13:48
  • @StanislavVolodarskiy, лучше бы написали просто T. Для плюсов это привычнее выглядит) – megorit Oct 09 '21 at 14:02
  • 4
    Каст к void означает вычислить выражение, а результирующее значение просто отбросить, 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:45
  • 4
    В коде же использование каста к void может применяться для подавления предупреждений компилятора при использовании атрибута nodiscard, а также для подавления вызова перегруженной версии оператора запятая вместо встроенной (см.: Why does std::transform and similar cast the 'for' loop increment to (void)?). – wololo Oct 09 '21 at 14:55
  • @wololo, *++iter точно будет отличаться от *iter++. Спасибо за ответы. – megorit Oct 09 '21 at 14:59
  • @megorit, "просто T" требует вполне определённую сигнатуру. (void) говорит "возвращайте что хотите, меня интересует только побочный эффект". Тут свободы больше. – Stanislav Volodarskiy Oct 09 '21 at 17:10
  • @wololo может оформите ответом? – αλεχολυτ Oct 13 '21 at 07:49

1 Answers1

3

Канонический оператор постинкремента в выражении i++ выполняет следующие действия:

  1. запомнить текущее значение i
  2. увеличить i на "единицу" (с учётом типа переменной)
  3. вернуть прежнее значение 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
  • Да, наверно, хотя для STL может быть предусмотрели. – megorit Oct 22 '21 at 17:09