Shiftoperator und Auswertungsreihenfolge?
-
Hallo,
ich hätte mal eine Frage zu
std::cout. Und zwar wenn ich etwas habe wie#include <iostream> int main() { int i = 1 << 2; std::cout << i << '\n'; // Ausgabe: 4 std::cout << 1 << 2 << '\n'; // Ausgabe: 12 }Woher weiß der Compiler jetzt was er hier machen soll bzw. ist die zweite Ausgabe nicht eigentlich undefiniert? Weil der Compiler könnte doch (nach meinem Verständnis) entweder
operator<<(std::cout, operator<<(1, 2))oder
operator<<(operator<<(std::cout, 1), 2)wählen, da die Auswertungsreihenfolge ja undefiniert ist, oder?
Dann würden aber jeweils unterschiedliche Werte (siehe oben) rauskommen! Also woher "weiß" der Compiler das bzw. warum macht er das hier immer "richtig"? Ist hier die Auswertungsreihenfolge doch definiert oder ist das "glücklicher Zufall"?
-
Die undefinierte Auswertungsreihenfolge spielt nur bei so etwas eine Rolle:
std::cout << ++i << ++i << '\n'; // UBWas in welcher Reihenfolge ausgegeben wird, ist wohl definiert.
-
Du musst zwischen Ausführung und Auswertung unterscheiden.
std::cout << f() << g() << '\n';ist das gleiche wie
((std::cout << f()) << g()) << '\n';da der <<-operator Linksassoziativ ist. (So wie a/b/c=(a/b)/c.)
Nicht definiert ist hingegen die Auswertung der Argumente. Man könnte entweder
auto&& x = f(); auto&& y = g(); ((std::cout << x) << y) << '\n';oder
auto&& y = g(); auto&& x = f(); ((std::cout << x) << y) << '\n';haben.
Das ist jedoch nicht UB, das ist nur unspezifiziert. Der Compiler darf sich entscheiden, wie er das machen will, aber solange dir das egal ist, hast du kein Problem.
UB kommt nur dann ins Spiel, wenn auf Werte mehrmals lesend und schreibend zugegriffen wird.
std::cout << ++i << ++i << '\n'; // UBDas ist UB (aber nur für primitive Typen!). Du kannst es dir so vorstellen, dass in
int x = i; i = x + 1; int y = i; i = y + 1;je nach Reihenfolge am Ende i nur um 1 erhöht wurde, was den Regeln widerspricht.
-
klammeraffe schrieb:
Man könnte entweder
auto&& x = f(); auto&& y = g(); ((std::cout << x) << y) << '\n';oder
auto&& y = g(); auto&& x = f(); ((std::cout << x) << y) << '\n';haben.
Ausserdem könnte evtl die Ausgabe von f() erfolgen bevor g() aufgerufen wird (was beobachtbar wäre, wenn g() selbst etwas ausgibt).
-
Ok, alles klar. Vielen Dank für die Antworten, hab glatt die Assoziativität bei der ganzen Geschichte vergessen. Jetzt macht das Sinn
