Considere este tema como una secuela del siguiente tema:
Entrega anterior
Comportamiento indefinido y puntos de secuencia
Repasemos esta expresión divertida y complicada (las frases en cursiva se tomaron del tema anterior * sonrisa *):
i += ++i;
Decimos que esto invoca un comportamiento indefinido. Supongo que cuando digo esto, asumimos implícitamente que el tipo de i
es uno de los tipos integrados.
¿Qué pasa si el tipo de i
es un tipo definido por el usuario? Digamos que su tipo es el Index
que se define más adelante en esta publicación (ver más abajo). ¿Seguiría invocando un comportamiento indefinido?
¿Si es así por qué? ¿No es equivalente a escribir i.operator+=(i.operator++());
o incluso sintácticamente más simple i.add(i.inc());
? ¿O también invocan un comportamiento indefinido?
Si no, ¿por qué no? Después de todo, el objeto i
se modifica dos veces entre puntos de secuencia consecutivos. Recuerde la regla general: una expresión puede modificar el valor de un objeto solo una vez entre "puntos de secuencia consecutivos" . Y si i += ++i
es una expresión, debe invocar un comportamiento indefinido. Si es así, sus equivalentes i.operator+=(i.operator++());
y i.add(i.inc());
también debe invocar un comportamiento indefinido que parece ser falso! (hasta donde yo entiendo)
¿O i += ++i
no es una expresión para empezar? Si es así, ¿qué es y cuál es la definición de expresión ?
Si es una expresión y, al mismo tiempo, su comportamiento también está bien definido, entonces implica que el número de puntos de secuencia asociados con una expresión depende de alguna manera del tipo de operandos involucrados en la expresión. ¿Estoy en lo correcto (incluso en parte)?
Por cierto, ¿qué tal esta expresión?
//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the subscript operator!
a[++i] = i; //Taken from the previous topic. But here type of `i` is Index.
También debe considerar esto en su respuesta (si conoce su comportamiento con seguridad). :-)
Es
++++++i;
bien definido en C ++ 03? Después de todo, esto es esto
((i.operator++()).operator++()).operator++();
class Index
{
int state;
public:
Index(int s) : state(s) {}
Index& operator++()
{
state++;
return *this;
}
Index& operator+=(const Index & index)
{
state+= index.state;
return *this;
}
operator int()
{
return state;
}
Index & add(const Index & index)
{
state += index.state;
return *this;
}
Index & inc()
{
state++;
return *this;
}
};
s
es un tipo definido por el usuario!)Respuestas:
Parece el código
i.operator+=(i.operator ++());
Funciona perfectamente bien con respecto a los puntos de secuencia. La sección 1.9.17 del estándar ISO C ++ dice esto sobre los puntos de secuencia y la evaluación de funciones:
Esto indicaría, por ejemplo, que
i.operator ++()
como parámetrooperator +=
tiene un punto de secuencia después de su evaluación. En resumen, dado que los operadores sobrecargados son funciones, se aplican las reglas de secuenciación normales.¡Gran pregunta, por cierto! Me gusta mucho cómo me estás obligando a entender todos los matices de un idioma que ya pensé que conocía (y pensé que pensé que sabía). :-)
fuente
http://www.eelis.net/C++/analogliterals.xhtml Me vienen a la mente literales analógicos
unsigned int c = ( o-----o | ! ! ! ! ! o-----o ).area; assert( c == (I-----I) * (I-------I) ); assert( ( o-----o | ! ! ! ! ! ! ! o-----o ).area == ( o---------o | ! ! ! o---------o ).area );
fuente
Como han dicho otros, su
i += ++i
ejemplo funciona con el tipo definido por el usuario, ya que está llamando a funciones, y las funciones comprenden puntos de secuencia.Por otro lado,
a[++i] = i
no es tan afortunado asumir quea
es su tipo de matriz básico, o incluso uno definido por el usuario. El problema que tiene aquí es que no sabemos qué parte de la expresión que contienei
se evalúa primero. Podría ser que++i
se evalúe, se pase aoperator[]
(o la versión sin procesar) para recuperar el objeto allí, y luego el valor dei
se pase a ese (que es después de que se incrementó i). Por otro lado, quizás el último lado se evalúe primero, se almacene para una asignación posterior y luego++i
se evalúe la parte.fuente
++i
y la lectura dei
en el RHS de la asignación, entonces el orden no estaría especificado. Debido a que uno de los ordenamientos permitidos hace esas dos cosas sin un punto de secuencia intermedio, el comportamiento no está definido.a
integrado y definido por el usuarioi
.Creo que está bien definido:
Del borrador del estándar C ++ (n1905) §1.9 / 16:
Tenga en cuenta la parte en negrita. Esto significa que de hecho hay un punto de secuencia después de la llamada de función de incremento (
i.operator ++()
) pero antes de la llamada de asignación compuesta (i.operator+=
).fuente
Bien. Después de repasar las respuestas anteriores, volví a pensar en mi propia pregunta, particularmente en esta parte que solo Noah intentó responder, pero no estoy completamente convencido con él.
Caso 1:
Si
a
es una matriz de tipo incorporado. Entonces lo que dijo Noah es correcto. Es decir,Entonces
a[++i]=i
invoca un comportamiento indefinido, o el resultado no está especificado. Sea lo que sea, ¡no está bien definido!PD: en la cita anterior,
tachadoes por supuesto mío.Caso 2:
Si
a
es un objeto de tipo definido por el usuario que sobrecarga eloperator[]
, entonces nuevamente hay dos casos.operator[]
función sobrecargada es un tipo integrado, de nuevoa[++i]=i
invoca un comportamiento indefinido o el resultado no está especificado.operator[]
función sobrecargada es un tipo definido por el usuario, entonces el comportamiento dea[++i] = i
está bien definido (hasta donde tengo entendido), ya que en este casoa[++i]=i
es equivalente a escribira.operator[](++i).operator=(i);
que es lo mismo quea[++i].operator=(i);
,. Es decir, la asignaciónoperator=
se invoca en el objeto devuelto dea[++i]
, que parece estar muy bien definido, ya que para el momento en que losa[++i]
retornos++i
ya se han evaluado, y luego el objeto devuelto llama a laoperator=
función y le pasa el valor actualizado dei
como argumento. Tenga en cuenta que hay un punto de secuencia entre estas dos llamadas . Y la sintaxis asegura que no haya competencia entre estas dos llamadas, yoperator[]
se invocaría primero, y consecutivamente, el argumento que se le++i
pasó, también se evaluaría primero.Piense en esto como
someInstance.Fun(++k).Gun(10).Sun(k).Tun();
en el que cada llamada de función consecutiva devuelve un objeto de algún tipo definido por el usuario. Para mí, esta situación se parece más a esto:,eat(++k);drink(10);sleep(k)
porque en ambas situaciones, existe un punto de secuencia después de cada llamada de función.Por favor corrígeme si estoy equivocado. :-)
fuente
k++
y nok
están separados por puntos de secuencia. Ambos pueden ser evaluados antes de o son evaluados. El lenguaje solo requiere que se evalúe antes , no que los argumentos se evalúen antes que los argumentos. Estoy volviendo a explicar lo mismo sin poder dar una referencia, así que no vamos a progresar desde aquí.Sun
Fun
Fun
Sun
Fun
Sun
Sun
ejecución, peroFun
el argumento++k
puede evaluarse antes o después de eso. Hay puntos de secuencia antes y después de laFun
ejecución, peroSun
el argumentok
puede evaluarse antes o después de eso. Por lo tanto, un caso posible es que ambosk
y++k
se evalúan antes queSun
oFun
se evalúan, por lo que ambos están antes de los puntos de secuencia de llamada de función, por lo que no hay un punto de secuencia que separek
y++k
.eat(i++);drink(10);sleep(i);
? ... incluso ahora, ¿podría decirse quei++
puede ser evaluado antes o después de eso?k
y++k
. En el ejemplo de comer / beber, no es como punto de secuencia entrei
yi++
.eat()
ysleep()
existe punto (s) de secuencia, pero entre hay argumentos ni siquiera uno. ¿Cómo pueden los argumentos de dos llamadas a funciones separadas por puntos de secuencia pertenecer a los mismos puntos de secuencia?