Зачем использовать ++ я вместо я ++ в случаях, когда значение не используется нигде в инструкции?

Мне хорошо известно, что в С++

int someValue = i++;
array[i++] = otherValue;

имеет различный эффект по сравнению с

int someValue = ++i;
array[++i] = otherValue;

но каждый раз я вижу инструкции с приращением префикса для for-loops или просто по своему усмотрению:

for( int i = 0; i < count; ++i ) {
     //do stuff
}

или

for( int i = 0; i < count; ) {
    //do some stuff;
    if( condition ) {
        ++i;
    } else {
        i += 4;
    }
}

В последних двух случаях ++i выглядит как попытка создать смарт-код. Я что-то наблюдаю? Есть ли причина использовать ++i вместо i++ в последних двух случаях?

Ответ 1

Если мы игнорируем силу привычки, "++ i" является более простой операцией концептуально: она просто добавляет ее к значению i, а затем использует ее.

i++, с другой стороны, это "взять исходное значение i, сохранить его как временное, добавить его в i, а затем вернуть временное". Это требует от нас сохранения старой ценности даже после обновления i.

И, как показал Конрад Рудольф, для использования i++ с пользовательскими типами могут быть затраты на производительность.

Итак, вопрос в том, почему не всегда просто по умолчанию ++i?

Если у вас нет причин использовать `i ++ ', зачем это делать? Почему вы по умолчанию выполняете операцию, которая сложнее рассуждать и может выполняться медленнее?

Ответ 2

Посмотрите на возможные реализации двух операторов в собственном коде:

// Pre-increment
T*& operator ++() {
    // Perform increment operation.
    return *this;
}

// Post-increment
T operator ++(int) {
    T copy = *this;
    ++*this;
    return copy;
}

Оператор postfix вызывает оператор префикса для выполнения своей собственной операции: по дизайну и в принципе префиксная версия всегда будет быстрее, чем постфиксная версия, хотя компилятор может оптимизировать это во многих случаях ( и особенно для встроенных типов).

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

Ответ 3

Как вы отметили - это не имеет значения для результата.

Существует оценка производительности для не-примитивных типов.

Также семантически использование pre-increment обычно становится более ясным, если вы показываете намерение теста, когда используется возвращаемое значение, поэтому лучше использовать его обычно, чем post-increment, чтобы избежать случайного тестирования старого значения.

Ответ 4

Есть одна причина, и это связано с перегруженными операторами. В перегруженной функции postincrement функция должна помнить предыдущее значение объекта, увеличивать его, а затем возвращать предыдущее значение. В функции preincrement функция может просто увеличивать объект, а затем возвращать ссылку на себя (новое значение).

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

Ответ 5

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

Ответ 6

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

Ответ 7

С++ FAQ Lite имеет приятное обсуждение i++ vs. ++i здесь:

[13.15] Что более эффективно: я ++ или ++ i?

В частности, относительно того, какую форму использовать в цикле for, часто задаваемые вопросы выражают предпочтение ++i. Вот текст:

Итак, если вы пишете i++ как инструкцию, а не как часть более крупное выражение, почему бы просто написать ++i вместо этого? Вы никогда не проигрываете ничего, и вы иногда что-то выигрываете. Программисты старой строки C используется для записи i++ вместо ++i. Например, они скажут: for (i = 0; i <10; i++).... Так как это использует i++ как инструкцию, а не как часть более крупное выражение, то вместо этого вы можете использовать ++i. Для симметрии, я лично защищаю этот стиль, даже если он не улучшить скорость, например, для встроенных типов и типов классов с постфиксные операторы, которые возвращают void.