Betrachten Sie dieses Thema als Fortsetzung des folgenden Themas:
Vorherige Rate
Undefiniertes Verhalten und Sequenzpunkte
Lassen Sie uns diesen lustigen und verschlungenen Ausdruck noch einmal betrachten (die kursiven Sätze stammen aus dem obigen Thema * smile *):
i += ++i;
Wir sagen, dies ruft undefiniertes Verhalten hervor. Ich nehme an, wenn wir dies sagen, nehmen wir implizit an, dass der Typ von i
einer der eingebauten Typen ist.
Was passiert , wenn die Art der i
ein benutzerdefinierter Typ? Angenommen, der Typ wird Index
später in diesem Beitrag definiert (siehe unten). Würde es immer noch undefiniertes Verhalten hervorrufen?
Wenn ja, warum? Ist es nicht gleichbedeutend mit Schreiben i.operator+=(i.operator++());
oder sogar syntaktisch einfacher i.add(i.inc());
? Oder rufen auch sie undefiniertes Verhalten hervor?
Wenn nein, warum nicht? Immerhin wird das Objekt zwischen aufeinanderfolgenden Sequenzpunkten zweimali
geändert . Bitte erinnern Sie sich an die Faustregel: Ein Ausdruck kann den Wert eines Objekts nur einmal zwischen aufeinanderfolgenden "Sequenzpunkten ändern . Und wenn es sich um einen Ausdruck handelt, muss er undefiniertes Verhalten aufrufen. Wenn ja, dann seine Äquivalente und muss auch undefiniertes Verhalten aufrufen, das scheint falsch zu sein! (soweit ich verstehe)i += ++i
i.operator+=(i.operator++());
i.add(i.inc());
Oder i += ++i
ist nicht ein Ausdruck von Anfang an? Wenn ja, was ist es dann und wie wird der Ausdruck definiert ?
Wenn es ein Ausdruck ist, und zugleich wird ihr Verhalten auch gut definiert, dann bedeutet dies , dass die Anzahl der Sequenzpunkte mit einem Ausdruck zugeordnet ist irgendwie auf das hängt Art des Operanden im Ausdruck beteiligt. Bin ich richtig (auch nur teilweise)?
Übrigens, wie wäre es mit diesem Ausdruck?
//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the subscript operator!
a[++i] = i; //Taken from the previous topic. But here type of `i` is Index.
Sie müssen dies auch in Ihrer Antwort berücksichtigen (wenn Sie das Verhalten sicher kennen). :-)
Ist
++++++i;
in C ++ 03 gut definiert? Immerhin ist dies das,
((i.operator++()).operator++()).operator++();
class Index
{
int state;
public:
Index(int s) : state(s) {}
Index& operator++()
{
state++;
return *this;
}
Index& operator+=(const Index & index)
{
state+= index.state;
return *this;
}
operator int()
{
return state;
}
Index & add(const Index & index)
{
state += index.state;
return *this;
}
Index & inc()
{
state++;
return *this;
}
};
s
benutzerdefinierter Typ ist!)Antworten:
Es sieht aus wie der Code
Funktioniert einwandfrei in Bezug auf Sequenzpunkte. In Abschnitt 1.9.17 des C ++ ISO-Standards wird dies über Sequenzpunkte und Funktionsbewertung gesagt:
Dies würde zum Beispiel anzeigen, dass der
i.operator ++()
als Parameter zuoperator +=
haben nach seiner Auswertung einen Sequenzpunkt hat. Kurz gesagt, da überladene Operatoren Funktionen sind, gelten die normalen Sequenzierungsregeln.Tolle Frage übrigens! Ich mag es wirklich, wie Sie mich zwingen, alle Nuancen einer Sprache zu verstehen, von der ich bereits dachte, dass ich sie kenne (und dachte, dass ich dachte, dass ich sie kenne). :-)
quelle
http://www.eelis.net/C++/analogliterals.xhtml Analoge Literale kommen mir in den Sinn
quelle
Wie bereits erwähnt,
i += ++i
funktioniert Ihr Beispiel mit dem benutzerdefinierten Typ, da Sie Funktionen aufrufen und Funktionen Sequenzpunkte umfassen.Auf der anderen Seite
a[++i] = i
ist es nicht so glücklich anzunehmen, dass diesa
Ihr grundlegender Array-Typ oder sogar ein benutzerdefinierter ist. Das Problem, das Sie hier haben, ist, dass wir nicht wissen, welcher Teil des Ausdrucksi
zuerst ausgewertet wird. Es könnte sein, dass++i
ausgewertet, anoperator[]
(oder die Rohversion) übergeben wird, um das Objekt dort abzurufen, und dann der Wert voni
an das übergeben wird (was ist, nachdem ich inkrementiert wurde). Andererseits wird möglicherweise zuerst die letztere Seite ausgewertet, für eine spätere Zuordnung gespeichert und dann das++i
Teil ausgewertet.quelle
++i
und dem Leseni
auf der rechten Seite der Zuordnung hätten, wäre die Reihenfolge nicht spezifiziert. Da eine der zulässigen Ordnungen diese beiden Dinge ohne dazwischenliegenden Sequenzpunkt ausführt, ist das Verhalten undefiniert.a
und integriertei
.Ich denke, es ist gut definiert:
Aus dem C ++ Standardentwurf (n1905) §1.9 / 16:
Beachten Sie den Teil, den ich fett gedruckt habe. Dies bedeutet, dass es zwar einen Sequenzpunkt nach dem Inkrementfunktionsaufruf (
i.operator ++()
), aber vor dem zusammengesetzten Zuweisungsaufruf (i.operator+=
) gibt.quelle
In Ordung. Nachdem ich frühere Antworten durchgesehen hatte, dachte ich über meine eigene Frage nach, insbesondere über diesen Teil, den nur Noah zu beantworten versuchte, aber ich bin nicht vollständig von ihm überzeugt.
Fall 1:
If
a
ist ein Array vom integrierten Typ. Dann ist das, was Noah gesagt hat, richtig. Das ist,So
a[++i]=i
Invokes-Verhalten ist nicht definiert, oder das Ergebnis ist nicht spezifiziert. Was auch immer es ist, es ist nicht genau definiert!PS: Im obigen Zitat
Durchschlagist natürlich meins.Fall 2:
Wenn
a
es sich um ein benutzerdefiniertes Objekt handelt, das das Objekt überlastetoperator[]
, gibt es erneut zwei Fälle.operator[]
Funktion ein integrierter Typ ist, wird erneut eina[++i]=i
undefiniertes Verhalten aufgerufen oder das Ergebnis wird nicht angegeben.operator[]
Funktion jedoch ein benutzerdefinierter Typ ist, ist das Verhalten vona[++i] = i
(soweit ich weiß) genau definiert, da in diesem Falla[++i]=i
das Schreiben gleichbedeutenda.operator[](++i).operator=(i);
ist mit ,a[++i].operator=(i);
. Das heißt, die Zuweisungoperator=
wird für das zurückgegebene Objekt von aufgerufena[++i]
, was sehr genau definiert zu sein scheint, da zum Zeitpunkt dera[++i]
Rückgabe++i
bereits ausgewertet wurde und das zurückgegebene Objekt dieoperator=
Funktion aufruft , die den aktualisierten Wert voni
als Argument an das Objekt zurückgibt . Beachten Sie, dass zwischen diesen beiden Aufrufen ein Sequenzpunkt liegt . Und die Syntax stellt sicher, dass zwischen diesen beiden Aufrufen keine Konkurrenz besteht, undoperator[]
würde zuerst aufgerufen werden, und nacheinander würde das darin übergebene Argument++i
auch zuerst ausgewertet werden.Stellen Sie sich das so vor, als würde
someInstance.Fun(++k).Gun(10).Sun(k).Tun();
jeder aufeinanderfolgende Funktionsaufruf ein Objekt eines benutzerdefinierten Typs zurückgeben. Für mich scheint diese Situation eher so zu sein:eat(++k);drink(10);sleep(k)
In beiden Situationen gibt es nach jedem Funktionsaufruf einen Sequenzpunkt.Bitte korrigieren Sie mich, wenn ich falsch liege. :-)
quelle
k++
undk
sind nicht durch Sequenzpunkte getrennt. Sie können beide vorSun
oderFun
ausgewertet werden. Die Sprache erfordert nur , dassFun
vorher bewertet wirdSun
, nicht, dassFun
die Argumente vor den Argumenten ausgewertet werdenSun
. Ich erkläre das Gleiche noch einmal, ohne einen Hinweis geben zu können, also werden wir von hier aus nicht weiterkommen.Sun
, aberFun
das Argument++k
kann vorher oder nachher ausgewertet werden. Es gibt Sequenzpunkte vor und nach der AusführungFun
, aberSun
das Argumentk
kann vorher oder nachher ausgewertet werden. Ein möglicher Fall ist daher, dass beidek
und++k
vor einemSun
oder beidenFun
ausgewertet werden und beide vor den Funktionsaufruf-Sequenzpunkten liegen, sodass kein Sequenzpunkt zwischenk
und getrennt wird++k
.eat(i++);drink(10);sleep(i);
? ... könnte man schon jetzt sagen,i++
kann vorher oder nachher ausgewertet werden?k
und++k
. Im Beispiel Essen / Trinken gibt es als Sequenzpunkt zwischeni
undi++
.eat()
undsleep()
existiert Sequenzpunkte, aber zwischen diesen Argumenten gibt es nicht einmal einen. Wie können Argumente zu zwei durch Sequenzpunkte getrennten Funktionsaufrufen zu denselben Sequenzpunkten gehören ?