librerias lenguaje funciones estructuras estructura ejemplos codigos codigo basicos c++ chaining order-of-evaluation operator-precedence

funciones - lenguaje c++ ejemplos



Orden de ejecución de C++ en el encadenamiento de métodos (4)

Creo que al compilar, antes de que se llamen realmente las funciones meth1 y meth2, se les han pasado los parámetros. Quiero decir cuando usas "c.meth1 (& nu) .meth2 (nu);" el valor nu = 0 se pasó a meth2, por lo que no importa si "nu" se cambia más tarde.

puedes probar esto:

#include <iostream> class c1 { public: c1& meth1(int* ar) { std::cout << "method 1" << std::endl; *ar = 1; return *this; } void meth2(int* ar) { std::cout << "method 2:" << *ar << std::endl; } }; int main() { c1 c; int nu = 0; c.meth1(&nu).meth2(&nu); getchar(); }

obtendrá la respuesta que deseas

La salida de este programa:

#include <iostream> class c1 { public: c1& meth1(int* ar) { std::cout << "method 1" << std::endl; *ar = 1; return *this; } void meth2(int ar) { std::cout << "method 2:"<< ar << std::endl; } }; int main() { c1 c; int nu = 0; c.meth1(&nu).meth2(nu); }

Es:

method 1 method 2:0

¿Por qué nu no es 1 cuando comienza meth2() ?


Creo que esta parte del borrador del estándar con respecto al orden de evaluación es relevante:

1.9 Ejecución del programa

...

  1. Excepto donde se indique, las evaluaciones de operandos de operadores individuales y de subexpresiones de expresiones individuales no tienen secuencia. Los cálculos del valor de los operandos de un operador se secuencian antes del cálculo del 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 es indefinido

y también:

5.2.2 Llamada de función

...

  1. [Nota: Las evaluaciones de la expresión postfix 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 , por lo que vemos claramente el desglose en la expresión de postfix y el argumento nu :

operator()(c.meth1(&nu).meth2, nu);

Las evaluaciones de la expresión y el argumento de postfix para la llamada de función final (es decir, la expresión de postfix 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 postfijo en el objeto escalar ar está secuenciado en relación con la evaluación del argumento de nu antes de la llamada a la función meth2 . 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 argumento nu de la llamada meth2 después de la llamada meth1 ; es libre de suponer que los efectos secundarios de meth1 afectan la evaluación nu .

El código de ensamblaje producido por lo anterior contiene la siguiente secuencia en la función main :

  1. La variable nu se asigna en la pila y se inicializa con 0.
  2. Un registro ( ebx en mi caso) recibe una copia del valor de nu
  3. Las direcciones de nu y c se cargan en registros de parámetros
  4. meth1 se llama
  5. El registro del valor de retorno y el valor de nu almacenado previamente en el registro ebx se cargan en registros de parámetros
  6. meth2 se llama

Críticamente, en el paso 5 anterior, el compilador permite que el valor almacenado en caché de nu del paso 2 se reutilice en la llamada de función a meth2 . Aquí no se tiene en cuenta la posibilidad de que nu haya sido modificado por la llamada a meth1 - ''comportamiento indefinido'' en acción.

NOTA: Esta respuesta ha cambiado en sustancia de su forma original. Mi explicación inicial en términos de efectos secundarios del cálculo de operandos que no se secuenciaron antes de la llamada a la función final fueron incorrectos, porque lo son. El problema es el hecho de que el cómputo de los operandos mismos está secuenciado indeterminadamente.


En el estándar C ++ de 1998, Sección 5, párrafo 4

Excepto donde se indique, el orden de evaluación de los operandos de operadores individuales y subexpresiones de expresiones individuales, y el orden en que se producen los efectos secundarios, no se especifica. Entre el punto de secuencia anterior y el siguiente, un objeto escalar tendrá su valor almacenado modificado como máximo una vez por la evaluación de una expresión. Además, se debe acceder al valor anterior solo para determinar el valor a almacenar. Los requisitos de este párrafo se cumplirán para cada ordenamiento permitido de las subexpresiones de una expresión completa; de lo contrario, el comportamiento es indefinido.

(He omitido una referencia a la nota al pie # 53 que no es relevante para esta pregunta).

Esencialmente, &nu debe evaluarse antes de llamar a c1::meth1() , y nu debe evaluarse antes de llamar a c1::meth2() . Sin embargo, no existe el requisito de que nu se evalúe antes de &nu (por ejemplo, se permite que nu se evalúe primero, luego &nu , y luego se c1::meth1() , que podría ser lo que está haciendo su compilador). Por lo tanto, no se garantiza que la expresión *ar = 1 en c1::meth1() se evalúe antes de evaluar nu en main() , para pasar a c1::meth2() .

Los estándares posteriores de C ++ (que actualmente no tengo en la PC que estoy usando esta noche) tienen esencialmente la misma cláusula.


Porque el orden de evaluación no está especificado.

Está viendo que nu en main se evalúa a 0 incluso antes de que se meth1 a meth1 . Este es el problema con el encadenamiento. Aconsejo no hacerlo.

Simplemente haga un programa agradable, simple, claro, fácil de leer y fácil de entender:

int main() { c1 c; int nu = 0; c.meth1(&nu); c.meth2(nu); }