Kod, alt ifadelerin belirlenmemiş değerlendirme sırasına bağlı olarak tanımlanmamış davranış sergiler, ancak tanımlanmamış davranışa neden olmaz, çünkü tüm yan etkiler bu durumda yan etkiler arasında bir sıralama ilişkisi getiren işlevler içinde yapılır .
Bu örnek, sorudaki kodla ilgili olarak aşağıdakileri söyleyen N4228: Deyimsel C ++ İçin İfade Değerlendirme Sırasını İyileştirme Teklifinde belirtilmiştir :
[...] Bu kod (Dil, 4 Programlama C ++ dünya çapında C ++ uzmanlar tarafından incelendi ve yayınlanmıştır inci baskı.) Oysa değerlendirmenin belirtilmemiş düzene savunmasızlığından bir araç [tarafından ancak son zamanlarda keşfedilmiştir .. .]
Detaylar
İşlevlere yönelik argümanların belirlenmemiş bir değerlendirme sırasına sahip olduğu birçok kişi için açık olabilir, ancak muhtemelen bu davranışın zincirleme işlev çağrılarıyla nasıl etkileştiği açık değildir. Bu vakayı ilk analiz ettiğimde bana açık değildi ve görünüşe göre tüm uzman eleştirmenler için de değildi .
İlk bakışta, her replace
birinin soldan sağa değerlendirilmesi gerektiğinden, karşılık gelen fonksiyon argüman gruplarının soldan sağa gruplar halinde değerlendirilmesi gerektiği görünebilir .
Bu yanlıştır, işlev bağımsız değişkenleri belirlenmemiş bir değerlendirme sırasına sahiptir, ancak zincirleme işlev çağrıları her işlev çağrısı için soldan sağa bir değerlendirme sırası getirse de, her işlev çağrısının argümanları yalnızca parçası oldukları üye işlev çağrısına göre önceden sıralanır nın-nin. Bu özellikle aşağıdaki çağrıları etkiler:
s.find( "even" )
ve:
s.find( " don't" )
şunlara göre belirsiz şekilde sıralanır:
s.replace(0, 4, "" )
iki find
çağrı , sonucunu değiştirecek replace
bir yan etkiye sahip s
olduğu için find
, uzunluğunu değiştirdiği için önemlidir s
. Bu nedenle replace
, iki find
çağrıya göre ne zaman değerlendirildiğine bağlı olarak sonuç farklı olacaktır.
Zincirleme ifadesine bakarsak ve bazı alt ifadelerin değerlendirme sırasını incelersek:
s.replace(0, 4, "" ).replace( s.find( "even" ), 4, "only" )
^ ^ ^ ^ ^ ^ ^ ^ ^
A B | | | C | | |
1 2 3 4 5 6
ve:
.replace( s.find( " don't" ), 6, "" );
^ ^ ^ ^
D | | |
7 8 9
Unutmayın, bunu görmezden geliyoruz 4
ve 7
daha fazla alt ifadeye bölünebiliriz. Yani:
A
öncesinde B
dizilenir, daha önce C
dizilenirD
1
için 9
belirsiz aşağıda belirtilenler dışında bazı diğer alt ifadeleri bakımından sıralanamadı
1
daha 3
önce sıralanacakB
4
daha 6
önce sıralanacakC
7
daha 9
önce sıralanacakD
Bu sorunun anahtarı şudur:
4
için 9
belirsiz göre dizilirB
İçin 4
ve buna 7
göre olası değerlendirme sırası, değerlendirme B
arasındaki clang
ve gcc
değerlendirme sırasındaki sonuçlardaki farkı açıklar f2()
. Benim testlerde clang
değerlendirir B
değerlendirmeden önce 4
ve 7
sırasında gcc
değerlendirir onu sonra. Her durumda neler olduğunu göstermek için aşağıdaki test programını kullanabiliriz:
#include <iostream>
#include <string>
std::string::size_type my_find( std::string s, const char *cs )
{
std::string::size_type pos = s.find( cs ) ;
std::cout << "position " << cs << " found in complete expression: "
<< pos << std::endl ;
return pos ;
}
int main()
{
std::string s = "but I have heard it works even if you don't believe in it" ;
std::string copy_s = s ;
std::cout << "position of even before s.replace(0, 4, \"\" ): "
<< s.find( "even" ) << std::endl ;
std::cout << "position of don't before s.replace(0, 4, \"\" ): "
<< s.find( " don't" ) << std::endl << std::endl;
copy_s.replace(0, 4, "" ) ;
std::cout << "position of even after s.replace(0, 4, \"\" ): "
<< copy_s.find( "even" ) << std::endl ;
std::cout << "position of don't after s.replace(0, 4, \"\" ): "
<< copy_s.find( " don't" ) << std::endl << std::endl;
s.replace(0, 4, "" ).replace( my_find( s, "even" ) , 4, "only" )
.replace( my_find( s, " don't" ), 6, "" );
std::cout << "Result: " << s << std::endl ;
}
Sonuç gcc
( canlı görün )
position of even before s.replace(0, 4, "" ): 26
position of don't before s.replace(0, 4, "" ): 37
position of even after s.replace(0, 4, "" ): 22
position of don't after s.replace(0, 4, "" ): 33
position don't found in complete expression: 37
position even found in complete expression: 26
Result: I have heard it works evenonlyyou donieve in it
Sonuç clang
( canlı görün ):
position of even before s.replace(0, 4, "" ): 26
position of don't before s.replace(0, 4, "" ): 37
position of even after s.replace(0, 4, "" ): 22
position of don't after s.replace(0, 4, "" ): 33
position even found in complete expression: 22
position don't found in complete expression: 33
Result: I have heard it works only if you believe in it
Sonuç Visual Studio
( canlı görün ):
position of even before s.replace(0, 4, "" ): 26
position of don't before s.replace(0, 4, "" ): 37
position of even after s.replace(0, 4, "" ): 22
position of don't after s.replace(0, 4, "" ): 33
position don't found in complete expression: 37
position even found in complete expression: 26
Result: I have heard it works evenonlyyou donieve in it
Standarttan detaylar
Alt ifadelerin değerlendirmelerinin sıralanmamış olduğunu belirtmedikçe, bunun taslak C ++ 11 standart bölüm 1.9
Program yürütme bölümünden olduğunu biliyoruz :
Belirtilenler dışında, tek tek operatörlerin işlenenlerinin ve tek tek ifadelerin alt ifadelerinin değerlendirmeleri sıralı değildir. [...]
ve bir fonksiyon çağrısının, fonksiyonun postfix ifadesi ve fonksiyon gövdesine ilişkin argümanlar ile ilişkisinden önce sıralı bir dizi getirdiğini biliyoruz, bölüm 1.9
:
[...] Bir işlev çağrılırken (işlev satır içi olsun ya da olmasın), herhangi bir bağımsız değişken ifadesiyle veya çağrılan işlevi belirten sonek ifadesiyle ilişkili her değer hesaplaması ve yan etki, her ifadenin veya ifadenin çalıştırılmasından önce sıralanır. çağrılan işlevin gövdesinde. [...]
Ayrıca, sınıf üyesi erişiminin ve dolayısıyla zincirlemenin, 5.2.5
Sınıf üyesi erişimi bölümünden soldan sağa doğru değerlendirileceğini biliyoruz :
[...] Nokta veya oktan önceki sonek ifadesi değerlendirilir; 64
bu değerlendirmenin sonucu, id ifadesi ile birlikte tüm sonek ifadesinin sonucunu belirler.
İd ifadesinin statik olmayan bir üye işlev olarak sona ermesi durumunda , bu ayrı bir alt ifade olduğundan, içindeki ifade listesinin değerlendirme sırasını belirtmez ()
. 5.2
Postfix ifadelerinden ilgili dilbilgisi :
postfix-expression:
postfix-expression ( expression-listopt)
postfix-expression . templateopt id-expression
C ++ 17 değişiklikleri
P0145r3: Deyimsel C ++ için İfade Değerlendirme Sırasını İyileştirme önerisi birkaç değişiklik yaptı. Sonek ifadeleri ve bunların ifade listesi için değerlendirme kurallarının sırasını güçlendirerek koda iyi tanımlanmış davranış veren değişiklikleri içerir .
[ifade. çağrı] s5 diyor ki:
Sonek ifadesi, ifade listesindeki her ifadeden ve herhangi bir varsayılan bağımsız değişkenden önce sıralanır . Her ilişkili değer hesaplaması ve yan etki dahil olmak üzere bir parametrenin başlatılması, diğer herhangi bir parametreninkine göre belirsiz bir şekilde sıralanır. [Not: Bağımsız değişken değerlendirmelerinin tüm yan etkileri, işleve girilmeden önce sıralanır (bkz. 4.6). —End note] [Örnek:
void f() {
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");
}
—Son örnek]
s.replace( s.replace( s.replace(0, 4, "" ).find( "even" ), 4, "only" ).find( " don't" ), 6, "" );