Uwaga z ciągami zapisów do strumienia
Ktoś się kiedyś dziwił, że fragment kodu
cout << obj.Incr() << " " << obj.Incr() << " " << obj.Incr() << " " << obj.Incr() << " " << obj.Incr() << " " << obj.Incr() << " " << obj.Incr() << " " << obj.Incr() << " " << obj.Incr() << " " << obj.Incr() << " " << obj.Incr() << endl;
(gdzie metoda Incr modyfikowała pewien atrybut klasy i zwracała opartą na nim wartość - w uproszczeniu można przyjąć że inkrementowała atrybut i zwracała ile on aktualnie wynosi) na jednych platformach wypisywał
0 1 2 3 4 5 6 7 8 9 10
a na innych
10 9 8 7 6 5 4 3 2 1
Wytłumaczenie jest proste: zapis
cout << obj.Incr();
jest tak naprawdę wywołaniem funkcji
operator<<(cout, obj.Incr())
zwracającej ostream&. W szczególności
cout << obj.Incr() << obj.Incr;
to tak naprawdę
operator<<( operator<<(cout, a.Incr() ), a.Incr() );
Kompilator ma swobodę przy ustalaniu kolejności obliczania parametrów w takim wyrażeniu. Niektóre obliczą podwyrażenia "od lewej", inne "od prawej" (poprawny byłby nawet kompilator, który kolejność obliczania podwyrażeń by sobie za każdym razem losował).
Wniosek? Trzeba uważać, gdy wpisujemy do strumieni wyniki funkcji posiadających efekty uboczne - i raczej nie stosować wówczas zapisu "łańcuchowego" tylko coś w stylu
cout << obj.Incr(); cout << obj.Incr(); // ...
- «Rzutowanie typów całkowitych wymaga dyscypliny
- Nie używaj funkcji isspace»
- ↑C++ - sztuczki i niebezpieczeństwa