Na seção de Operações semelhantes a STL da The C ++ Programming Language 4ª edição de Bjarne Stroustrup, o código a seguir é usado como um exemplo de encadeamento :36.3.6
void f2()
{
std::string s = "but I have heard it works even if you don't believe in it" ;
s.replace(0, 4, "" ).replace( s.find( "even" ), 4, "only" )
.replace( s.find( " don't" ), 6, "" );
assert( s == "I have heard it works only if you believe in it" ) ;
}
A declaração falha em gcc
( veja ao vivo ) e Visual Studio
( veja ao vivo ), mas não falha ao usar o Clang ( veja ao vivo ).
Por que estou obtendo resultados diferentes? Algum desses compiladores está avaliando incorretamente a expressão de encadeamento ou este código exibe alguma forma de comportamento não especificado ou indefinido ?
c++
c++11
language-lawyer
operator-precedence
unspecified-behavior
Shafik Yaghmour
fonte
fonte
s.replace( s.replace( s.replace(0, 4, "" ).find( "even" ), 4, "only" ).find( " don't" ), 6, "" );
cout << a << b << c
≡operator<<(operator<<(operator<<(cout, a), b), c)
é apenas um pouco menos feio.Respostas:
O código exibe um comportamento não especificado devido à ordem não especificada de avaliação de subexpressões, embora não invoque um comportamento indefinido, pois todos os efeitos colaterais são feitos dentro de funções que introduzem uma relação de sequenciamento entre os efeitos colaterais neste caso.
Este exemplo é mencionado na proposta N4228: Refining Expression Evaluation Order for Idimatic C ++ que diz o seguinte sobre o código em questão:
Detalhes
Pode ser óbvio para muitos que os argumentos para funções têm uma ordem de avaliação não especificada, mas provavelmente não é tão óbvio como esse comportamento interage com chamadas de funções encadeadas. Não era óbvio para mim quando analisei este caso pela primeira vez e, aparentemente, nem para todos os revisores especialistas .
À primeira vista pode parecer que, uma vez que cada um
replace
deve ser avaliado da esquerda para a direita, os grupos de argumentos de função correspondentes também devem ser avaliados como grupos da esquerda para a direita.Isso está incorreto, os argumentos da função têm uma ordem de avaliação não especificada, embora as chamadas de função de encadeamento introduzam uma ordem de avaliação da esquerda para a direita para cada chamada de função, os argumentos de cada chamada de função só são sequenciados antes em relação à chamada de função de membro da qual fazem parte do. Em particular, isso afeta as seguintes chamadas:
e:
que são sequenciados indeterminadamente em relação a:
as duas
find
chamadas podem ser avaliadas antes ou depois dereplace
, o que é importante, pois tem um efeito colaterals
em de uma forma que alteraria o resultado defind
, ele altera a duração des
. Portanto, dependendo de quando issoreplace
for avaliado em relação às duasfind
chamadas, o resultado será diferente.Se olharmos para a expressão de encadeamento e examinarmos a ordem de avaliação de algumas das subexpressões:
e:
Observe que estamos ignorando o fato de que
4
e7
pode ser dividido em mais subexpressões. Assim:A
é sequenciado antes doB
qual é sequenciado antes doC
qual é sequenciado antesD
1
a9
são sequenciados indeterminadamente em relação a outras subexpressões com algumas das exceções listadas abaixo1
para3
serem sequenciados antesB
4
para6
serem sequenciados antesC
7
para9
serem sequenciados antesD
A chave para este problema é que:
4
a9
serem sequenciados indeterminadamente em relação aB
A ordem potencial de escolha da avaliação para
4
e7
com respeito aB
explica a diferença nos resultados entreclang
egcc
durante a avaliaçãof2()
. Em meus testesclang
avaliaB
antes de avaliar4
e7
enquantogcc
avalia depois. Podemos usar o seguinte programa de teste para demonstrar o que está acontecendo em cada caso:Resultado para
gcc
( veja ao vivo )Resultado para
clang
( veja ao vivo ):Resultado para
Visual Studio
( veja ao vivo ):Detalhes do padrão
Sabemos que, a menos que especificado, as avaliações de subexpressões não são sequenciadas, isto é do esboço da seção padrão C ++ 11
1.9
Execução do programa que diz:e sabemos que uma chamada de função introduz um relacionamento sequenciado antes da expressão e argumentos pós-fixados das chamadas de função com relação ao corpo da função, da seção
1.9
:Também sabemos que o acesso do membro da classe e, portanto, o encadeamento será avaliado da esquerda para a direita, na seção
5.2.5
Acesso do membro da classe que diz:Observe que, no caso em que a expressão id acaba sendo uma função-membro não estática, ela não especifica a ordem de avaliação da lista de expressões dentro de,
()
visto que é uma subexpressão separada. A gramática relevante das5.2
expressões Postfix :Mudanças C ++ 17
A proposta p0145r3: Refinando a ordem de avaliação de expressão para C ++ idiomático fez várias alterações. Incluindo mudanças que dão ao código um comportamento bem especificado, fortalecendo a ordem das regras de avaliação para expressões postfix e sua lista de expressões .
[expr.call] p5 diz:
fonte
"even"
,"don't"
e as várias instâncias de nãos
são sequenciadas entre si.foo().func( bar() )
, ele pode ligarfoo()
antes ou depois de ligarbar()
. A expressão pós-fixada éfoo().func
. Os argumentos e a expressão pós-fixada são sequenciados antes do corpo defunc()
, mas não sequenciados em relação um ao outro.Pretende-se acrescentar informações sobre o assunto em relação ao C ++ 17. A proposta ( Refining Expression Evaluation Order for Idimatic C ++ Revisão 2 ) para
C++17
abordar o problema citando o código acima foi como amostra.Como sugerido, adicionei informações relevantes da proposta e para citar (destaques a minha):
O artigo sugeriu mudar a
C++17
regra de avaliação da ordem de expressão que foi influenciada porC
e existe há mais de três décadas. Ele propunha que a linguagem deveria garantir expressões contemporâneas ou correr o risco de "armadilhas e fontes de bugs obscuros, difíceis de encontrar" , como o que aconteceu com o espécime de código acima.A proposta
C++17
é exigir que toda expressão tenha uma ordem de avaliação bem definida :O código acima é compilado com sucesso usando
GCC 7.1.1
eClang 4.0.0
.fonte