Постинкремент vs преинкремент



В С++ есть замечательные операторы инкремента(например вот в питоне их нет). Преинкремент увеличивает значение числа на единицу и возвращает ссылку на него. А постинкремент по идее создает временную переменную равную текущему значению числа, увеличивает число на единицу и возвращает по значению временную переменную. Поэтому кстати результат преинкремента - lvalue(возвращается как бы rvalue, но потом приводится к lvalue, потому что ссылка), то есть его можно использовать слева от знака равно, а постинкремента - rvalue, и его уже нельзя использовать слева от равно. Последнее верно в любом случае, это семантика языка. Но вот что насчет реализации?



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



Возьмем простенький пример:



int main()

{

for ( int i = 0; i < 10; ++i) {}

return 0;

}



Казалось бы, конкретно для этого примера от перестановки мест слагаемых, сумма не меняется. Мы можем использовать и тот и другой оператор, результат будет тем же. Может компилятору хватит мозгов, чтобы выяснить это и не генерировать неэффективный код?



И реально, мозгов хватает.



movl $0, -4(%rbp)

.L3:

cmpl $9, -4(%rbp)

jg .L2

addl $1, -4(%rbp)

jmp .L3

.L2:

movl $0, %eax

popq %rbp

.cfi_def_cfa 7, 8

ret



Такой ассемблер компилятор генерирует о обоих случаях. И это даже без оптимизаций!! С оптимизациями код был бы пустым, потому что мы ничего полезного не делаем. Но я проверял для более сложных случаев, когда код цикла реально генерировался, и с оптимизациями. Все одинаково.

Кладем нолик в память для i, сравниваем его с девяткой, если девятка больше или равна i, то прибавляем единичку и прыгаем обратно в цикл. Если девятка меньше чем i, то прыгаем на выход из main.



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



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



Stay relaxed. Stay cool.