Подтвердить что ты не робот

Имеет ли смысл оптимизировать я ++ как ++ i, чтобы избежать временной переменной?

Кто-то сказал мне, что я могу написать

for (iterator it = somecontainer.begin(); it != somecontainer.end(); ++it)

вместо

for (iterator it = somecontainer.begin(); it != somecontainer.end(); it++)

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

4b9b3361

Ответ 1

Хорошая привычка вступать, поскольку итераторы могут быть сколь угодно сложными. Для индексов vector::iterator или int нет, это не повлияет.

Компилятор никогда не сможет устранить (исключить) копию, потому что копирование исключает только промежуточные временные, а не неиспользуемые. Для облегченных объектов, включая большинство итераторов, компилятор может оптимизировать код, реализующий копию. Однако это не всегда очевидно, когда это невозможно. Например, после приращения istream_iterator<string> гарантируется копирование последнего объекта string. В реализации string, не относящейся к ссылке, которая будет выделять, копировать и сразу освобождать память. Эта проблема, скорее всего, применима к более тяжелым классам без итератора, поддерживающим post-increment.

Конечно, нет недостатка. Кажется, это стало преобладающим стилем за последние десять-два года.

Ответ 2

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

Но я сомневаюсь, что стоит вернуться и изменить существующий код, чтобы использовать префиксную версию.

Ответ 3

Нет. (Стилистически я предпочитаю это, потому что мой родной язык - это английский, который в основном является языком с глаголом-предшествующим существительным, и поэтому "increment it" читается легче, чем "it increment", но этот стиль и субъективный.)

Однако, если вы не изменяете содержимое somecontainer в своем цикле, вы можете рассмотреть возможность возврата возвращаемого значения somecontainer.end() во временную переменную. То, что вы там делаете, на самом деле вызовет функцию в каждом цикле.

Ответ 4

Да, это концептуально правильная вещь. Вам все равно, если он i++ или ++i? Нет, нет. Какая из них лучше? Второй вариант лучше, поскольку он потенциально быстрее. Таким образом, вы выбираете второй (pre-increment).

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

Ответ 5

Это зависит от типа, к которому вы применяете operator++. Если вы говорите о определенном пользователем типе, это будет связано с копированием всего UDT, что может быть очень дорого.

Однако, если вы говорите о встроенном типе, то, вероятно, вообще никакой разницы.

Вероятно, полезно привыкнуть к использованию preincrement там, где это возможно, даже когда postincrement в порядке, просто чтобы быть в привычке и дать вашему коду последовательный взгляд. Вы должны полагаться на компилятор, чтобы оптимизировать некоторые вещи для вас, но нет никаких оснований для того, чтобы сделать работу более трудной, чем это должно быть без всякой причины.

Ответ 6

Посмотрите, как обычно используется пост-инкремент в пользовательском типе:

MyIterator operator++(int) {
    MyIterator retval(*this);
    ++*this;
    return retval;
}

Итак, у нас есть два вопроса: может ли мой компилятор оптимизировать это в случаях, когда возвращаемое значение не используется, и мой компилятор оптимизирует это в случаях, когда возвращаемое значение не используется?

Что касается "может ли это?", то, безусловно, есть случаи, когда он не может. Если код не встроен, нам не повезло. Если конструктор копирования MyIterator имеет наблюдаемые побочные эффекты, тогда нам не повезло - команда-конструктор копирования позволяет возвращать возвращаемые значения и копии временных рядов, поэтому, по крайней мере, значение может быть скопировано только один раз. Но он не позволяет возвращать значения вообще не. В зависимости от вашего компилятора распределение памяти вполне может быть наблюдаемым побочным эффектом.

Конечно, итераторы обычно должны быть встроены, а итераторы обычно легки и не будут прилагать никаких усилий для копирования. Пока мы в порядке с этими двумя пунктами, я думаю, что мы в бизнесе - компилятор может встроить код, заметить, что retval не используется и что его создание является свободным от побочных эффектов и удаляет его. Это просто оставляет предварительный приращение.

Что касается "не так ли?" - попробуйте в своем компиляторе. Меня не беспокоит, потому что я всегда использую предварительный приращение, где эти два эквивалентны: -)

Для "нужно ли мне учитывать эту оптимизацию при написании кода?", я бы сказал не как таковой, но что преждевременная пессимизация столь же плоха, как и преждевременная оптимизация. Просто потому, что один (в лучшем случае) пустая трата времени не означает, что другая благородна - не уходите с пути, чтобы сделать ваш код медленнее только потому, что вы можете. Если кто-то искренне предпочитает видеть i++ в цикле, то вряд ли когда-либо сделать их код медленнее, поэтому они могут оптимизировать для эстетики, если они должны. Лично я бы предпочел, чтобы люди улучшили свой вкус...

Ответ 7

На самом деле это будет иметь значение в неоптимизированном коде, то есть в строках отладки.