Creo que esta parte del borrador del estándar con respecto al orden de evaluación es relevante:
1.9 Ejecución del programa
...
- Excepto donde se indique, las evaluaciones de operandos de operadores individuales y de subexpresiones de expresiones individuales no están secuenciadas. Los cálculos de valor de los operandos de un operador se secuencian antes del cálculo de valor del resultado del operador. Si un efecto secundario en un objeto escalar no está secuenciado en relación con otro efecto secundario en el mismo objeto escalar o un cálculo de valor utilizando el valor del mismo objeto escalar, y no son potencialmente concurrentes, el comportamiento no está definido
y también:
5.2.2 Llamada a función
...
- [Nota: Las evaluaciones de la expresión de sufijo y de los argumentos no están secuenciadas entre sí. Todos los efectos secundarios de las evaluaciones de argumentos se secuencian antes de ingresar la función - nota final]
Entonces, para su línea c.meth1(&nu).meth2(nu);
, considere lo que está sucediendo en el operador en términos del operador de llamada de función para la llamada final a meth2
, de modo que veamos claramente el desglose en la expresión y el argumento de sufijo nu
:
operator()(c.meth1(&nu).meth2, nu);
Las evaluaciones de la expresión de sufijo y el argumento para la llamada de función final (es decir, la expresión de sufijo c.meth1(&nu).meth2
y nu
) no están secuenciadas entre sí según la regla de llamada de función anterior. Por lo tanto, el efecto secundario del cálculo de la expresión de sufijo en el objeto escalar no ar
está secuenciado en relación con la evaluación del argumento nu
antes de la meth2
llamada a la función. Según la regla de ejecución del programa anterior, este es un comportamiento indefinido.
En otras palabras, no es necesario que el compilador evalúe el nu
argumento de la meth2
llamada después de la meth1
llamada; es libre de asumir que no hay efectos secundarios que meth1
afecten la nu
evaluación.
El código ensamblador producido por lo anterior contiene la siguiente secuencia en la main
función:
- La variable
nu
se asigna en la pila y se inicializa con 0.
- Un registro (
ebx
en mi caso) recibe una copia del valor denu
- Las direcciones de
nu
y c
se cargan en registros de parámetros
meth1
se llama
- El registro del valor de retorno y el valor previamente almacenado en caché de
nu
en el ebx
registro se cargan en los registros de parámetros
meth2
se llama
Críticamente, en el paso 5 anterior, el compilador permite que el valor en caché nu
del paso 2 se reutilice en la llamada de función a meth2
. Aquí se ignora la posibilidad de que nu
pueda haber sido cambiado por la llamada a meth1
- 'comportamiento indefinido' en acción.
NOTA: Esta respuesta ha cambiado sustancialmente desde su forma original. Mi explicación inicial en términos de los efectos secundarios del cálculo de operandos que no se secuenciaron antes de la llamada final a la función fue incorrecta, porque lo son. El problema es el hecho de que el cálculo de los propios operandos tiene una secuencia indeterminada.