Зачем использовать ++i вместо i++ в тех случаях, когда значение не используется где-либо еще в выражении?

Я хорошо знаю, что в C++

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

имеет другой эффект по сравнению с

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

но время от времени я вижу операторы с приставкой префикса в циклах for или просто по их собственному:

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++ в двух последних случаях?

7 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

C++ FAQ Lite имеет хорошее обсуждение i++ против ++i Вот:

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

В частности, в отношении какой формы использовать в for цикл, FAQ выражает предпочтение ++i, Вот текст:

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

Другие вопросы по тегам