Die Ausgabe dieses Programms:
#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);
}
Ist:
method 1
method 2:0
Warum ist nu
nicht 1, wenn meth2()
startet?
c++
chaining
operator-precedence
Moises Viñas
quelle
quelle
nu
,&nu
undc
auf den Stapel in dieser Reihenfolge, dann invokemeth1
, drücken Sie das Ergebnis auf den Stapel, dann invokemeth2
, während eine registerbasierten Aufrufkonvention wollen , würde Ladenc
und&nu
in Register, aufrufenmeth1
,nu
in ein Register laden , dann aufrufenmeth2
.Antworten:
Weil die Bewertungsreihenfolge nicht angegeben ist.
Sie sehen
nu
inmain
der Auswertung,0
bevor überhauptmeth1
aufgerufen wird. Dies ist das Problem bei der Verkettung. Ich rate davon ab.Machen Sie einfach ein schönes, einfaches, klares, leicht zu lesendes und leicht verständliches Programm:
quelle
<<
für die Ausgabe und "Objekt-Builder" für komplexe Objekte mit zu vielen Argumenten für die Konstruktoren - aber sie mischen sich sehr schlecht mit Ausgabeargumenten.meth1
undmeth2
ist definiert, aber die Auswertung der Parameter fürmeth2
kann erfolgen, bevormeth1
...?meth2(meth1(c, &nu), nu)
Ich denke, dieser Teil des Standardentwurfs bezüglich der Reihenfolge der Bewertung ist relevant:
und auch:
Überlegen Sie sich also für Ihre Zeile
c.meth1(&nu).meth2(nu);
, was im Operator in Bezug auf den Funktionsaufrufoperator für den letzten Aufruf von geschiehtmeth2
, damit wir die Aufteilung in den Postfix-Ausdruck und das Postfix-Argument deutlich sehennu
:Die Auswertungen des Postfix-Ausdrucks und des Arguments für den endgültigen Funktionsaufruf (dh des Postfix-Ausdrucks
c.meth1(&nu).meth2
undnu
) sind gemäß der obigen Funktionsaufrufregel relativ zueinander nicht sequenziert . Daher ist der Nebeneffekt der Berechnung des Postfix-Ausdrucks auf dasar
Skalarobjekt relativ zur Argumentauswertung vonnu
vor demmeth2
Funktionsaufruf nicht sequenziert . Nach der obigen Programmausführungsregel ist dies ein undefiniertes Verhalten.Mit anderen Worten, der Compiler muss das
nu
Argument für denmeth2
Aufruf nach demmeth1
Aufruf nicht auswerten. Es ist frei, keine Nebenwirkungen anzunehmen, die sichmeth1
auf dienu
Auswertung auswirken .Der oben erzeugte Assembler-Code enthält die folgende Sequenz in der
main
Funktion:nu
wird auf dem Stapel zugewiesen und mit 0 initialisiert.ebx
in meinem Fall) erhält eine Kopie des Wertes vonnu
nu
undc
werden in Parameterregister geladenmeth1
wird genanntnu
in demebx
Register werden in Parameterregister geladenmeth2
wird genanntEntscheidend ist, dass der Compiler in Schritt 5 oben zulässt, dass der zwischengespeicherte Wert
nu
von Schritt 2 im Funktionsaufruf an wiederverwendet wirdmeth2
. Hier wird die Möglichkeit außer Acht gelassen, dienu
durch den Aufruf vonmeth1
"undefiniertes Verhalten" in Aktion geändert wurde .HINWEIS: Diese Antwort hat sich im Wesentlichen von ihrer ursprünglichen Form geändert. Meine anfängliche Erklärung in Bezug auf die Nebenwirkungen der Operandenberechnung, die nicht vor dem letzten Funktionsaufruf sequenziert wurden, war falsch, weil sie es sind. Das Problem ist die Tatsache, dass die Berechnung der Operanden selbst unbestimmt sequenziert ist.
quelle
meth1
zuvor ausgeführt wurdemeth2
, aber der Parameter fürmeth2
ist ein Wert von, dernu
vor dem Aufruf von in einem Register zwischengespeichert wurdemeth1
- dh der Compiler hat die möglichen Nebenwirkungen ignoriert, d. H. im Einklang mit meiner Antwort.c.meth1(&nu).meth2
) und die Auswertung des Arguments für diesen Aufruf (nu
) sind im Allgemeinen nicht sequenziert, aber 1) ihre Nebenwirkungen werden alle vor dem Eintritt inmeth2
und 2) sequenziert, dac.meth1(&nu)
es sich um einen Funktionsaufruf handelt wird es mit der Auswertung von unbestimmt sequenziertnu
. Im Innernmeth2
, wenn es irgendwie einen Zeiger auf die Variable in erhaltenmain
, wäre es immer zu sehen , 1.meth2
, wie in Punkt 3 der von Ihnen zitierten Referenzseite angegeben (die Sie auch nicht richtig zitiert haben).Im C ++ - Standard von 1998, Abschnitt 5, Abs. 4
(Ich habe einen Verweis auf Fußnote 53 weggelassen, der für diese Frage nicht relevant ist.)
&nu
Muss im Wesentlichen vor dem Anruf ausgewertetc1::meth1()
werden undnu
muss vor dem Anruf ausgewertet werdenc1::meth2()
. Es gibt jedoch keine Anforderung,nu
die zuvor ausgewertet werden muss&nu
(z. B. ist es zulässig, dassnu
zuerst ausgewertet&nu
wird und dannc1::meth1()
aufgerufen wird - was möglicherweise Ihr Compiler tut). Es ist daher nicht garantiert, dass der Ausdruck*ar = 1
inc1::meth1()
ausgewertet wird, bevornu
inmain()
ausgewertet wird, um an übergeben zu werdenc1::meth2()
.Spätere C ++ - Standards (die ich derzeit nicht auf dem PC habe, den ich heute Abend verwende) haben im Wesentlichen dieselbe Klausel.
quelle
Ich denke, beim Kompilieren wurden die Parameter an sie übergeben, bevor die Funktionen meth1 und meth2 wirklich aufgerufen werden. Ich meine, wenn Sie "c.meth1 (& nu) .meth2 (nu);" Der Wert nu = 0 wurde an meth2 übergeben, daher spielt es keine Rolle, ob "nu" später geändert wird.
Sie können dies versuchen:
es wird die Antwort bekommen, die Sie wollen
quelle