2013-07-01 18 views
18

Parlare nel contesto dello standard C++ 11 (che non ha più un concetto di punti di sequenza, come sai) voglio capire come sono definiti due esempi più semplici.Ordine di valutazione e comportamento non definito

int i = 0; 

i = i++; // #0 

i = ++i; // #1 

ci sono due temi su SO che spiegano questi esempi nel contesto C++ 11. Here è stato detto che #0 invoca UB e #1 è ben definito. Here è stato detto che entrambi gli esempi non sono definiti. Questa ambiguità mi confonde molto. Ho letto questo ben strutturato reference tre volte ma l'argomento sembra essere troppo complicato per me.

.

Analizziamo l'esempio #0: i = i++;.

citazioni corrispondenti sono:

  • Il valore di calcolo incorporato postincremento e postdecrement operatori viene sequenziato prima del suo effetto collaterale.

  • L'effetto collaterale (modifica dell'argomento sinistra) del built-in operatore di assegnazione e di tutti gli operatori di assegnazione composti incorporati viene sequenza dopo il calcolo del valore (ma non gli effetti collaterali) di sia a sinistra e giuste argomentazioni, e viene sequenziato prima che il valore calcolo dell'espressione di assegnazione (cioè, prima di tornare il riferimento all'oggetto modificato)

  • Se un effetto collaterale su un oggetto scalare è non in sequenza rispetto ad un altro lato effetto sullo stesso oggetto scalare, il comportamento non è definito.

Come ho capito, l'effetto collaterale di l'operatore di assegnazione non viene sequenziato con effetti collaterali di esso è rimasto e gli argomenti giusti. Pertanto, l'effetto collaterale dell'operatore di assegnazione non viene sequenziato con gli effetti collaterali di i++. Quindi #0 invoca un UB.

.

Analizziamo l'esempio #1: i = ++i;.

citazioni corrispondenti sono:

  • L'effetto collaterale dei incorporati preincremento e predecremento operatori viene sequenza prima della computazione valore (regola implicita causa a definizione come assegnazione composta)

  • L'effetto collaterale (modifica dell'argomento a sinistra) dell'operatore di assegnazione incorporato e di tutti gli operatori di assegnazione composti incorporati è sequenziati dopo il calcolo del valore (ma non gli effetti collaterali) di argomenti destro e sinistro, e viene sequenziato prima che il valore calcolo dell'espressione di assegnazione (cioè, prima di tornare il riferimento all'oggetto modificato)

  • Se un effetto collaterale su un oggetto scalare è ingiustificato rispetto ad un altro effetto collaterale sullo stesso oggetto scalare, il comportamento non è definito.

non riesco a vedere, come questo esempio è diverso dal #0. Questo sembra essere un UB per me per lo stesso motivo di #0. L'effetto collaterale dell'assegnazione non è sequenziato con l'effetto collaterale di ++i. Sembra essere un UB. L'argomento sopra suggerito dice che è ben definito. Perché?

.

Domanda: come posso applicare le regole citate per determinare la UB degli esempi. Una spiegazione il più semplice possibile sarebbe molto apprezzata. Grazie!

+5

Avrei una conversazione piuttosto franca con il programmatore se ho visto questo nel codice di produzione. Sì, è una curvatura intellettuale, ma non riesco a vedere molto merito nel pensare troppo a questo. Detto questo, +1 per la domanda ben scritta e leggerò in dettaglio la risposta accettata. – Bathsheba

+2

@Bathsheba Concordo sul fatto che quegli esempi di codice non dovrebbero probabilmente mai essere in un codice reale, ma penso che la comprensione dello standard aiuterà a determinare l'UB in altri esempi. – Kolyunya

+0

Ti ho upvoted sulla qualità della domanda e, come te, attendo una risposta di qualità simile. – Bathsheba

risposta

9

Poiché le tue quotazioni non sono direttamente dallo standard, cercherò di fornire una risposta dettagliata citando le parti pertinenti dello standard. Le definizioni di "effetti collaterali" e "valutazione" si trova nel paragrafo 1.9/12:

Accesso a un oggetto designato da uno glvalue volatile (3.10), modifica di un oggetto, chiamando una libreria I/O Funzione, o chiamando una funzione che fa una di queste operazioni sono tutti gli effetti , che sono cambiamenti nello stato dell'ambiente di esecuzione. La valutazione di un'espressione (o una sottoespressione) in generale include entrambi i calcoli del valore (compresa la determinazione dell'identità di un oggetto per la valutazione glvalue e il recupero di un valore precedentemente assegnato a un oggetto per la valutazione del valore nominale) e l'inizio di effetti collaterali.

La parte successiva rilevante è il paragrafo 1.9/15:

Salvo quando diversamente indicato, valutazioni di operandi dei singoli operatori e di sottoespressioni delle singole espressioni sono non in sequenza. [...] I calcoli del valore degli operandi di un operatore sono sequenziati prima del calcolo del valore del risultato dell'operatore. Se un effetto collaterale su un oggetto scalare viene annullato rispetto a un altro effetto collaterale sullo stesso oggetto scalare o un calcolo del valore che utilizza il valore dello stesso oggetto scalare, il comportamento non è definito.

Ora vediamo come applicare questo ai due esempi.

i = i++; 

Questa è la forma di incremento postfix e la sua definizione è riportata nel paragrafo 5.2.6. La frase più rilevante è la seguente:

Il calcolo del valore dell'espressione ++ viene sequenziato prima della modifica dell'oggetto operando.

Per l'espressione di assegnazione vedere il paragrafo 5.17. La parte rilevante:

In tutti i casi, l'assegnazione viene eseguita in sequenza dopo il calcolo del valore degli operandi di destra e di sinistra e prima del calcolo del valore dell'espressione di assegnazione.

Utilizzando tutte le informazioni di cui sopra, la valutazione dell'intera espressione è (questo ordine non è garantito dallo standard!):

  • valore calcolo di i++ (lato destro)
  • valore calcolo di i (lato sinistro)
  • modifica del i (effetto collaterale di ++)
  • modifica del i (effetto collaterale di =)

Tutte le garanzie standard è che il calcolo del valore dei due gli operandi sono sequenziati prima del calcolo del valore dell'espressione di assegnazione. Ma il calcolo del valore sul lato destro è solo "leggendo il valore di i" e non modificando i, le due modifiche (effetti collaterali) non sono sequenziate l'una rispetto all'altra e otteniamo un comportamento indefinito.

E il secondo esempio?

i = ++i; 

Qui la situazione è molto diversa. Si trova la definizione di incremento prefisso nel paragrafo 5.3.2. La parte rilevante è:

Se x non è di tipo bool, l'espressione ++ x è equivalente a x + = 1.

Sostituendo questo, la nostra espressione è equivalente a

i = (i += 1) 

Alzando l'operatore di assegnamento composto += a 5.17/7 otteniamo che i += 1 equivale a i = i + 1 tranne che i viene valutata solo una volta. Quindi, l'espressione in questione diventa finalmente

i = (i = (i + 1))

Ma sappiamo già da sopra che il calcolo del valore del = viene sequenza dopo il calcolo del valore degli operandi e gli effetti collaterali sono sequenziati prima dei calcoli del valore di =. Così otteniamo un ordine ben definito di valutazione:

  1. valore di calcolo della i + 1 (e i - lato sinistro di espressione interna) (# 1)
  2. iniziato effetto collaterale di interno =, cioè modificare "interna "i
  3. valore di calcolo (i = i + 1), che è il 'nuovo' valore i
  4. avviare effetto collaterale esterno =, cioè modificare 'esterno' i
  5. valore di calcolo della piena espressione.

(# 1): Qui, i viene valutata solo una volta, dal momento che i += 1 equivale a i = i + 1 tranne che i viene valutata solo una volta (5.17/7).

+0

Grazie per una risposta dettagliata. Se ho capito bene l'ordine di valutazione del secondo esempio è: argomenti di calcolo dell'assegnazione interna ==> effetti collaterali di assegnazione interna ==> valore di calcolo di inner assignmnet ==> effetti collaterali di assegnazione esterna ==> valore di calcolo di incarico esterno. – Kolyunya

+0

@Kolyunya Ho aggiornato la mia risposta. Spero che questo risponda alla tua domanda. – MWid

+0

Grazie. Penso di averlo capito. – Kolyunya

8

La differenza principale è che ++i è definito come i += 1, così

i = ++i; 

è uguale a:

i = (i += 1); 

Poiché gli effetti collaterali del gestore += vengono inviati in sequenza prima valore computazione dell'operatore, la modifica effettiva di i in ++i viene sequenziata prima dell'assegnazione esterna. Questo segue direttamente dalle sezioni che si citano: "L'effetto collaterale (modifica dell'argomento a sinistra) dell'operatore assegnato assegnazione e di tutti gli operatori di assegnazione composto incorporato è sequenziato dopo il calcolo del valore (ma non il effetti collaterali) di entrambi gli argomenti sinistra e destra, ed è sequenziato prima della valore calcolo dell'espressione di assegnazione (cioè prima restituire il riferimento all'oggetto modificato)"

Ciò è dovuto al l'operatore di assegnazione nidificata ; l'operatore di assegnazione (esterno) impone solo in precedenza il calcolo del valore dei suoi operandi, non i loro effetti collaterali. (Ma del corso , non annulla il sequenziamento imposto diversamente.)

E come si fa notare indirettamente, questo è nuovo per C++ 11; precedentemente, entrambi erano indefiniti. Le versioni precedenti di C++ utilizzavano punti di sequenza, anziché sequenziati in precedenza, e lì non era un punto di sequenza in nessuno degli operatori di assegnazione. . (I l'impressione che l'intento era che gli operatori che risultato in un Ivalue hanno un valore che viene sequenziato dopo ogni effetti collaterali In precedenza C++, l'espressione *&++i era comportamento indefinito; in C++ 11, è garantito uguale a ++i.)

+1

Grazie per la risposta James. Potresti spiegare perché questo è vero, probabilmente con alcuni riferimenti? Poiché gli effetti collaterali dell'operatore + = sono sequenziati prima del calcolo del valore dell'operatore, la modifica effettiva di i in ++ i è sequenziata prima dell'assegnazione esterna. – Kolyunya

+1

La citazione nella mia domanda afferma che l'effetto collaterale del primo compito in 'i = (i + = 1);' non è sequenziato con l'effetto collaterale del suo argomento destro - '(i + = 1)'. Lo frainteso? – Kolyunya

+1

Non so se sono solo io a non essere abbastanza intelligente per capirlo, ma probabilmente una spiegazione molto dettagliata potrebbe aiutare me e altre persone. – Kolyunya