Acho que esta parte do projeto de norma em relação à ordem de avaliação é relevante:
1.9 Execução do Programa
...
- Exceto onde indicado, as avaliações de operandos de operadores individuais e de subexpressões de expressões individuais não são sequenciadas. Os cálculos do valor dos operandos de um operador são sequenciados antes do cálculo do valor do resultado do operador. Se um efeito colateral em um objeto escalar não for sequenciado em relação a outro efeito colateral no mesmo objeto escalar ou um cálculo de valor usando o valor do mesmo objeto escalar, e eles não forem potencialmente simultâneos, o comportamento é indefinido
e também:
5.2.2 Chamada de função
...
- [Nota: As avaliações da expressão pós-fixada e dos argumentos são todas sem seqüência em relação umas às outras. Todos os efeitos colaterais das avaliações de argumento são sequenciados antes que a função seja inserida - nota final]
Portanto, para sua linha c.meth1(&nu).meth2(nu);
, considere o que está acontecendo no operador em termos do operador de chamada de função para a chamada final para meth2
, para que possamos ver claramente a divisão na expressão pós-fixada e no argumento nu
:
operator()(c.meth1(&nu).meth2, nu);
As avaliações da expressão pós-fixada e do argumento para a chamada de função final (ou seja, a expressão pós-fixada c.meth1(&nu).meth2
e nu
) não são sequenciadas entre si de acordo com a regra de chamada de função acima. Portanto, o efeito colateral do cálculo da expressão pós-fixada no objeto escalar não ar
é sequenciado em relação à avaliação do argumento nu
anterior à meth2
chamada da função. Pela regra de execução do programa acima, este é um comportamento indefinido.
Em outras palavras, não há nenhum requisito para o compilador avaliar o nu
argumento da meth2
chamada após a meth1
chamada - ele é livre para assumir que nenhum efeito colateral meth1
afeta a nu
avaliação.
O código de montagem produzido acima contém a seguinte sequência na main
função:
- A variável
nu
é alocada na pilha e inicializada com 0.
- Um cadastro (
ebx
no meu caso) recebe uma cópia do valor denu
- Os endereços de
nu
e c
são carregados nos registros de parâmetros
meth1
é chamado
- O registo valor de retorno eo valor previamente armazenado em cache de
nu
no ebx
registo são carregados em registradores de parâmetros
meth2
é chamado
De forma crítica, na etapa 5 acima, o compilador permite que o valor em cache da nu
etapa 2 seja reutilizado na chamada de função para meth2
. Aqui, ele desconsidera a possibilidade de que nu
pode ter sido alterado pela chamada para meth1
- 'comportamento indefinido' em ação.
NOTA: Esta resposta mudou substancialmente de sua forma original. Minha explicação inicial em termos de efeitos colaterais do cálculo do operando não sendo sequenciado antes da chamada da função final estava incorreta, porque eles estão. O problema é o fato de que o cálculo dos próprios operandos é sequenciado de forma indeterminada.