pre-incremento vs. post-incremento

3

Nella Guida di stile di Google C ++ si dice:

Preincrement and Predecrement

Use prefix form (++i) of the increment and decrement operators with iterators and other template objects.

When a variable is incremented (++i or i++) or decremented (--i or i--) and the value of the expression is not used, one must decide whether to preincrement (decrement) or postincrement (decrement).

When the return value is ignored, the "pre" form (++i) is never less efficient than the "post" form (i++), and is often more efficient. This is because post-increment (or decrement) requires a copy of i to be made, which is the value of the expression. If i is an iterator or other non-scalar type, copying i could be expensive. Since the two types of increment behave the same when the value is ignored, why not just always pre-increment?

The tradition developed, in C, of using post-increment when the expression value is not used, especially in for loops. Some find post-increment easier to read, since the "subject" (i) precedes the "verb" (++), just like in English.

For simple scalar (non-object) values there is no reason to prefer one form and we allow either. For iterators and other template types, use pre-increment.

Non devo assolutamente essere d'accordo con "Quando il valore di ritorno viene ignorato, il form" pre "( ++i ) non è mai meno efficiente del modulo" post "( i++ ), ed è spesso più efficiente Questo perché il post-incremento (o decremento) richiede una copia di i da produrre, che è il valore dell'espressione. "

Almeno quando si tratta di puntatori (e i registri dei puntatori possono essere usati come numeri interi).

Tornando ai miei giorni dell'hardware, c'erano molte istruzioni macchina che avevano una forma di modalità di indirizzamento con post-incremento, post-decremento e pre-decremento integrati. Le modalità post-inc e post-dec costano 0 cicli di istruzione supplementari più di nessun incremento. L'incremento si è verificato dopo che l'istruzione è stata eseguita mentre il prossimo opcode veniva recuperato, ma il pre-dec ha richiesto un ciclo di istruzioni aggiuntivo prima che il registro venisse utilizzato. Quindi, non lo capisco davvero. Qualcuno può spiegare il caso di Google a me?

    
posta robert bristow-johnson 07.10.2017 - 05:00
fonte

2 risposte

10

Potresti avere ragione riguardo ai puntatori.

Tuttavia:

  • C non è C ++. Il C ++ garantisce una semantica molto precisa, specialmente quando vengono fatte delle copie, poiché questo influenza RAII: in C ++, le copie sono un effetto osservabile.

  • Non tutti gli iteratori sono puntatori, ma possono essere oggetti più complessi. Ad esempio, gli iteratori di output come std::back_insert_iterator o iterators su strutture di dati non contigue come std::unordered_map .

I really must disagree with "When the return value is ignored, the "pre" form (++i) is never less efficient than the "post" form (i++), and is often more efficient. This is because post-increment (or decrement) requires a copy of i to be made, which is the value of the expression."

at least when it comes to pointers.

Non c'è nulla di cui non essere d'accordo qui. Il " ++i non è mai meno efficiente di i++ " significa solo che possono essere ugualmente efficienti (come argomentate per i puntatori). Ma se c'è una differenza, quindi ++i dovrebbe funzionare meglio.

Per essere precisi, si sostiene che il post-decremento potrebbe essere più efficiente su architetture particolari che garantiscono un post-decremento più efficiente rispetto al pre-decremento. Tuttavia, tali considerazioni si applicano solo quando si sviluppa per una particolare microarchitettura. Per il codice portatile, le garanzie fornite dalla lingua stessa sono più importanti. AFAIK né x86 né le famiglie di istruzioni dell'istruzione ARM includono un'istruzione speciale post-incremento.

Naturalmente è possibile creare un contro-esempio in cui l'operatore di pre-incremento esegue volutamente il lavoro non necessario, ma per una consistente e minima definizione di post-incremento e pre-incremento, quindi il post-incremento può (e probabilmente dovrebbe) essere implementato in termini di pre-incremento:

struct X {
  ...
  // pre-increment
  X& operator++() { ...; return *this; }

  // post-increment
  X operator++(int) {
    X copy(*this);
    operator++();
    return copy;
  }
};

Poiché il post-incremento deve restituire lo stato dell'oggetto prima dell'esecuzione dell'incremento, una copia dello stato prima dell'incremento è inevitabile per i tipi definiti dall'utente.

Naturalmente un compilatore sufficientemente intelligente può essere in grado di allineare il post-incremento ed evitare la copia (assumendo un banale copione di copie) sfruttando le regole di sequenziamento e la regola as-if, ma nel migliore dei casi torniamo a "ugualmente" efficiente”.

    
risposta data 07.10.2017 - 09:14
fonte
0

Invece di:

"Alcuni trovano più facile leggere il post-incremento, poiché il 'soggetto' (i) precede il 'verbo' (++), proprio come in inglese."

Prova sia per la leggibilità che per l'efficienza:

Alcuni trovano il pre-incremento più facile da leggere, poiché "l'oggetto" (i) segue il "verbo" (++), proprio come in inglese. (Questo è un esempio di "soggetto compreso".)

Leggi "++ i" come "incrementa i".

    
risposta data 19.12.2018 - 21:42
fonte

Leggi altre domande sui tag