El resultado 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 meth2()
comienza?
c++
chaining
operator-precedence
Moises Viñas
fuente
fuente
nu
,&nu
yc
en la pila en ese orden, luego invocarmeth1
, empujar el resultado a la pila, luego invocarmeth2
, mientras que una convención de llamadas basada en registros querría cargarc
y&nu
en registros, invocarmeth1
, cargarnu
en un registro, luego invocarmeth2
.Respuestas:
Porque el orden de evaluación no está especificado.
Estás viendo
nu
enmain
ser evaluado0
antes incluso demeth1
ser llamado. Este es el problema del encadenamiento. Aconsejo no hacerlo.Simplemente cree un programa agradable, simple, claro, fácil de leer y de entender:
fuente
<<
para la salida y "constructores de objetos" para objetos complejos con demasiados argumentos para los constructores, pero se mezcla muy mal con los argumentos de salida.meth1
ymeth2
está definido, pero la evaluación del parámetro parameth2
puede ocurrir antes de quemeth1
se llame ...?meth2(meth1(c, &nu), nu)
Creo que esta parte del borrador del estándar con respecto al orden de evaluación es relevante:
y también:
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 ameth2
, de modo que veamos claramente el desglose en la expresión y el argumento de sufijonu
: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
ynu
) 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 noar
está secuenciado en relación con la evaluación del argumentonu
antes de lameth2
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 lameth2
llamada después de lameth1
llamada; es libre de asumir que no hay efectos secundarios quemeth1
afecten lanu
evaluación.El código ensamblador producido por lo anterior contiene la siguiente secuencia en la
main
función:nu
se asigna en la pila y se inicializa con 0.ebx
en mi caso) recibe una copia del valor denu
nu
yc
se cargan en registros de parámetrosmeth1
se llamanu
en elebx
registro se cargan en los registros de parámetrosmeth2
se llamaCrí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 ameth2
. Aquí se ignora la posibilidad de quenu
pueda haber sido cambiado por la llamada ameth1
- '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.
fuente
meth1
se ejecuta antesmeth2
, pero el parámetro parameth2
es un valor denu
caché en un registro antes de la llamada ameth1
, es decir, el compilador ha ignorado los posibles efectos secundarios, que es coherente con mi respuesta.c.meth1(&nu).meth2
) y la evaluación del argumento de esa llamada (nu
) generalmente no tienen secuencia, pero 1) sus efectos secundarios están todos secuenciados antes de la entradameth2
y 2) ya quec.meth1(&nu)
es una llamada de función , tiene una secuencia indeterminada con la evaluación denu
. En el interiormeth2
, si de alguna manera obtiene un puntero a la variable enmain
, siempre verá 1.meth2
, como se indica en el elemento 3 de la página de referencia de cpp que está citando (que también se olvidó de citar correctamente).En el estándar C ++ de 1998, Sección 5, párrafo 4
(He omitido una referencia a la nota al pie # 53 que no es relevante para esta pregunta).
Esencialmente,
&nu
debe evaluarse antes de llamarc1::meth1()
ynu
debe evaluarse antes de llamarc1::meth2()
. Sin embargo, no hay ningún requisito quenu
deba evaluarse antes&nu
(por ejemplo, se permite quenu
se evalúe primero, luego&nu
, y luegoc1::meth1()
se llame, que podría ser lo que está haciendo su compilador). Por lo tanto, no se garantiza que la expresión*ar = 1
inc1::meth1()
se evalúe antesnu
de quemain()
se evalúe in para poder pasarla ac1::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.
fuente
Creo que al compilar, antes de que las funciones meth1 y meth2 se llamen realmente, se les han pasado los parámetros. Me refiero a cuando usa "c.meth1 (& nu) .meth2 (nu);" el valor nu = 0 se ha pasado a meth2, por lo que no importa si "nu" se cambia más tarde.
puedes probar esto:
obtendrá la respuesta que desea
fuente