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
...
- 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
...
- [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
:
-
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
yc
se cargan en registros de parámetros -
meth1
se llama -
El registro del valor de retorno y el
valor
de
nu
almacenado previamente en el registroebx
se cargan en registros de parámetros -
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);
}