2011-07-14 41 views
8

Sospecho que el siguiente encadenamiento de funciones daría como resultado una secuencia no especificada de acuerdo con los estándares de C++ (suponga C++ 0x). Solo quiero una confirmación y si alguien puede dar una explicación, lo agradecería.¿Este código está bien definido?

#include <iostream> 

struct TFoo 
{ 
    TFoo(int) 
    { 
     std::cout<<"TFoo"<<std::endl; 
    }; 
    TFoo foobar1(int) 
    { 
     std::cout<<"foobar1"<<std::endl; 
     return *this; 
    }; 
    TFoo foobar2(int) 
    { 
     std::cout<<"foobar2"<<std::endl; 
     return *this; 
    }; 
    static int bar1() 
    { 
     std::cout<<"bar1"<<std::endl; 
     return 0; 
    }; 
    static int bar2() 
    { 
     std::cout<<"bar2"<<std::endl; 
     return 0; 
    }; 
    static int bar3() 
    { 
     std::cout<<"bar3"<<std::endl; 
     return 0; 
    } 
}; 

int main(int argc, char *argv[]) 
{ 
    // is the sequence well defined for bar1, bar2 and bar3? 
    TFoo(TFoo::bar1()).foobar1(TFoo::bar2()).foobar2(TFoo::bar3()); 
} 

* edición: especificador __fastcall eliminado debido a que las funciones (no es necesario/relevante para la cuestión).

+1

No me refiero a ser tan quisquilloso, pero hablando técnicamente '__fastcall' no es parte de la especificación de C++, así que no creo que la especificación tenga nada que decir al respecto. ¿Hay alguna razón por la que lo incluiste aquí? ¿O la pregunta es específicamente sobre la interacción de '__fastcall' con los puntos de secuencia? – templatetypedef

Respuesta

8

La orden de evaluación no está especificada. La sección pertinente del proyecto de C++0x spec es 1.9, párrafos 14 y 15:

14 Todo efecto valor de cálculo y el lado asociado con un -expresión completa se secuenciaron antes de cada efecto del valor de cálculo y secundarios asociados con la próxima expresión completa a evaluar.

15 Salvo que se indique lo contrario, las evaluaciones de operandos de operadores individuales y de subexpresiones de expresiones individuales no se han secuenciado.

Aquí el correspondiente plena expresión es:

TFoo(TFoo::bar1()).foobar1(TFoo::bar2()).foobar2(TFoo::bar3()); 

Y lo que la evaluación de sus subexpresiones son unsequenced (a menos que haya una excepción señalado en alguna parte que me perdí).

Estoy bastante seguro de que las normas anteriores incluyen el lenguaje que tiene el mismo efecto pero en términos de "puntos de secuencia".

[editar]

El párrafo 15 también dice:

Cuando se llama a una función (si la función está en línea), todos los efectos de valor computación y secundarios asociados con cualquier expresión discusión, o con la expresión de postfijo que designa la función llamada, se secuencia antes de la ejecución de cada expresión o declaración en el cuerpo de la función llamada. [Nota: los cómputos de valor y los efectos secundarios asociados con las diferentes expresiones de argumentos no se han secuenciado.- nota final]

una "expresión de sufijo que designa la función llamada" es algo así como el foo().bar en foo().bar().

La "nota" aquí simplemente aclara que la orden de evaluación del argumento es no una excepción al valor predeterminado de "orden no especificada". Por deducción, tampoco es el orden de evaluación asociado con la "expresión de postfijo que designa la función llamada"; o si lo prefiere, el orden de evaluación de la expresión para el argumento this. (Si hubiera una excepción, este sería el lugar natural para especificarlo. O posiblemente la sección 5.2.2 que se refiere a las llamadas a funciones. Ninguna sección dice nada sobre el orden de evaluación para este ejemplo, por lo que no está especificado.)

+0

'Excepto donde se indique' - esto deja bastante para pensar ... –

+0

@Zach: De hecho. Pero si planea depender del orden de evaluación, debe encontrar una regla específica que establezca el orden. C/C++ siempre ha sido flexible al especificar esto para dar a los compiladores y CPU espacio para reordenar las cosas para el rendimiento. – Nemo

+1

IMO importante mencionar es que 'foobar1' se llama antes de' foobar2'. Entonces, el orden está restringido (porque el resultado de 'foobar1()' necesita ser evaluado antes de ser utilizado como un operando de 'operator', 1.9p15), aunque en su mayoría no especificado con respecto a las otras llamadas. –

1

Sí, el orden de evaluación de los argumentos de la función no está especificado.

Para mí, gcc 4.5.2 en Linux produce

bar3 
bar2 
bar1 
TFoo 
foobar1 
foobar2 

pero tañido ++ en Linux y gcc 3.4.6 en Solaris producen

bar1 
TFoo 
bar2 
foobar1 
bar3 
foobar2 

Analizar un ejemplo más simple, es una llamada TFoo(0).foobar1(TFoo::bar2()); a TFoo::foobar1 que toma dos argumentos: el resultado de la subexpresión TFoo(0) (como el argumento oculto this) y el resultado de la subexpresión Tfoo::bar2(). Para mí, gcc ejecuta primero bar2(), luego el constructor de TFoo, y luego llama a foobar1(), mientras que clang ++, por ejemplo, ejecuta primero el constructor de TFoo, luego bar2() y luego llama al foobar1().

+0

Sé que el orden de evaluación de los argumentos de la función no está especificado, pero en este caso, es realmente el orden del argumento eval de función (singular) para cada función encadenada (que tiene su orden especificado) que está en cuestión. ¿Alguna norma relevante para decir esto es realmente no especificada? –

+0

@Zach Saw: Las funciones miembro también toman 'this' como argumento implícito. – Cubbi

+0

cuidado para elaborar? ¿eso significa que el orden no está especificado también para las funciones encadenadas (foobar1 y foobar2)? –

Cuestiones relacionadas