A saída deste 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);
}
É:
method 1
method 2:0
Por que nu
não é 1 quando meth2()
começa?
c++
chaining
operator-precedence
Moises Viñas
fonte
fonte
nu
,&nu
ec
para a pilha nessa ordem, então invocarmeth1
, empurrar o resultado para a pilha e então invocarmeth2
, enquanto uma convenção de chamada baseada em registrador gostaria de carregarc
e&nu
em registradores, invocarmeth1
, carregarnu
em um registrador e então invocarmeth2
.Respostas:
Porque a ordem de avaliação não é especificada.
Você está vendo
nu
emmain
ser avaliado0
antes mesmo demeth1
ser chamado. Este é o problema do encadeamento. Eu aconselho não fazer isso.Basta fazer um programa agradável, simples, claro, fácil de ler e entender:
fonte
<<
para saída e "construtores de objeto" para objetos complexos com muitos argumentos para os construtores - mas combina muito mal com argumentos de saída.meth1
emeth2
é definida, mas a avaliação do parâmetro parameth2
pode acontecer antes demeth1
ser chamada ...?meth2(meth1(c, &nu), nu)
Acho que esta parte do projeto de norma em relação à ordem de avaliação é relevante:
e também:
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 parameth2
, para que possamos ver claramente a divisão na expressão pós-fixada e no argumentonu
: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
enu
) 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ãoar
é sequenciado em relação à avaliação do argumentonu
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 dameth2
chamada após ameth1
chamada - ele é livre para assumir que nenhum efeito colateralmeth1
afeta anu
avaliação.O código de montagem produzido acima contém a seguinte sequência na
main
função:nu
é alocada na pilha e inicializada com 0.ebx
no meu caso) recebe uma cópia do valor denu
nu
ec
são carregados nos registros de parâmetrosmeth1
é chamadonu
noebx
registo são carregados em registradores de parâmetrosmeth2
é chamadoDe 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 parameth2
. Aqui, ele desconsidera a possibilidade de quenu
pode ter sido alterado pela chamada parameth1
- '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.
fonte
meth1
é executado antesmeth2
, mas o parâmetro parameth2
é um valornu
armazenado em cache em um registro antes da chamada parameth1
- ou seja, o compilador ignorou os efeitos colaterais potenciais, que é consistente com minha resposta.c.meth1(&nu).meth2
) e a avaliação do argumento para essa chamada (nu
) geralmente não são sequenciados, mas 1) seus efeitos colaterais são todos sequenciados antes da entrada emmeth2
e 2) já quec.meth1(&nu)
é uma chamada de função , é sequenciado indeterminadamente com a avaliação denu
. Por dentrometh2
, se de alguma forma obtivesse um ponteiro para a variável emmain
, sempre veria 1.meth2
, conforme observado no item 3 da página cppreference que você está citando (que você também deixou de citar corretamente).No padrão C ++ de 1998, Seção 5, parágrafo 4
(Omiti uma referência à nota de rodapé # 53 que não é relevante para esta questão).
Essencialmente,
&nu
deve ser avaliado antes da chamadac1::meth1()
enu
deve ser avaliado antes da chamadac1::meth2()
. Não há, entretanto, nenhum requisito que devenu
ser avaliado antes&nu
(por exemplo, é permitido quenu
seja avaliado primeiro, então&nu
, e entãoc1::meth1()
seja chamado - o que pode ser o que seu compilador está fazendo). A expressão*ar = 1
in,c1::meth1()
portanto, não tem garantia de ser avaliada antes denu
inmain()
ser avaliada, a fim de ser passada parac1::meth2()
.Os padrões C ++ posteriores (que não tenho no PC que estou usando hoje à noite) têm essencialmente a mesma cláusula.
fonte
Eu acho que ao compilar, antes que as funções meth1 e meth2 sejam realmente chamadas, os parâmetros foram passados para eles. Quero dizer, quando você usa "c.meth1 (& nu) .meth2 (nu);" o valor nu = 0 foi passado para meth2, então não importa se "nu" foi alterado posteriormente.
você pode tentar isto:
obterá a resposta que você deseja
fonte