2010-05-28 14 views
6

El código siguiente muestra esta diferencia:¿La precedencia del operador en C++ difiere para los punteros y los iteradores?

#include <iostream> 
#include <string> 

int main() 
{ 
     char s[] = "ABCD"; 
     std::string str(s); 

     char *p = s; 
     while(*p) { 
       *p++ = tolower(*p);   // <-- incr after assignment 
     } 
     std::cout << s << std::endl; 

     std::string::iterator it = str.begin(), end = str.end(); 
     while(it != end) { 
       *it++ = tolower(*it);  // <-- incr before assignment ? 
     } 
     std::cout << str << std::endl; 

     return 0; 
} 

produce una salida:

abcd 
bcd 

si separamos operación de asignación y el operador de la subasta:

while(it != end) { 
    *it = tolower(*it);  // <-- incr before assignment ? 
    it++; 
} 

la salida será el esperado.

¿Qué pasa con el código original?

$ g++ --version 
g++ (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125) 
Copyright (C) 2004 Free Software Foundation, Inc. 

Respuesta

9

El problema es que el orden de evaluación de los argumentos de operator= está especificado. Esto está de acuerdo con el estándar C++ 5.2.2/8. Considere lo siguiente:

*it++ = tolower(*it); 

es igual a

operator=(*it++, tolower(*it)); 

ahora *it++ puede ser calculada antes tolower(*it) y viceversa.

+1

Es peor que el comportamiento no especificado, no hay un comportamiento indefinido en la expresión original. Existe un posible ordenamiento de subexpresiones donde se evalúa 'it ++' (es decir, una escritura en 'it') antes de que' it' se lea en la evaluación de parámetros para 'tolower' sin ningún punto de secuencia intermedio. Esto puede suceder ya sea que el operador de asignación sea o no una llamada de función o no. De cualquier forma 5 [expr], el párrafo 8 confirma que esto es un comportamiento indefinido. –

2
*it++ = tolower(*it); 
*p++ = tolower(*p); 

Ambas líneas invocar un comportamiento indefinido. No se puede modificar el valor de una variable más de una vez en una sola instrucción (++ modifica una vez, operator = modifica dos veces).

Por lo tanto, el hecho de que obtenga valores diferentes no es sorprendente.

+0

'operator ++' y 'operator =' aplicados aquí a dos variables diferentes en cada caso. No hay UB. –

+0

@Kirill V. Lyadvinsky: Hay un comportamiento indefinido tanto para el puntero como para el caso del iterador, ya que existe una secuencia posible de las operaciones si no hay punto de secuencia intermedio entre la lectura del valor de 'it' (o' p') para el operador '*' en el RHS de la asignación y la operación de incremento en el LHS de la asignación. –

+0

En cualquier caso, las líneas son ciertamente confusas y no quisiera verlas escritas así de todos modos :-) –

2

La gramática funciona exactamente igual para punteros e iteradores. Las operaciones implícitas por los operadores se convierten en llamadas a funciones para objetos del tipo de clase (como la mayoría de los iteradores).

El problema con su código no está con la precedencia del operador, en ambas líneas no hay secuencia entre la operación de incremento y la segunda lectura de la misma variable que se incrementa en otra parte de la instrucción. Debido a esto, tiene un comportamiento indefinido por lo que podría ver cualquier comportamiento de su programa, incluidos los resultados que está viendo.

*p++ = tolower(*p); 

*it++ = tolower(*it); 

Debe reformular esta afirmación de forma que se defina la secuencia. Supongo que quieres algo como esto.

char c = tolower(*p); 
*p++ = c; 
+0

+1: su complemento funciona, pero tengo una vaga comprensión de por qué –

+0

Justificación para downvote, ¿alguien? –

+0

+1 para una buena sugerencia. Puede ser una sugerencia incorrecta al final, pero es útil. Considere agregar enlaces al Estándar. –

Cuestiones relacionadas