Nicht statisches Mitglied als Standardargument einer nicht statischen Mitgliedsfunktion [duplicate]
Lesezeit: 7 Minuten
Armen Tsirunyan
struct X
{
X():mem(42){}
void f(int param = mem) //ERROR
{
//do something
}
private:
int mem;
};
Kann mir jemand nur einen Grund nennen, warum dies in C++ illegal ist?! Das heißt, ich weiß, dass es sich um einen Fehler handelt, ich weiß, was der Fehler bedeutet, ich kann einfach nicht verstehen, warum das illegal sein sollte!
@marcog: Obwohl ich zustimmen kann, dass dies etwas verwandt ist, glaube ich, dass dies überhaupt kein Duplikat ist …
– Armen Tsirunyan
27. Dezember 10 um 14:49 Uhr
@Armen Die dort akzeptierte Antwort beantwortet Ihre Frage etwas: Der Compiler kennt die Instanz beim Analysieren des Standardarguments nicht.
– moinudin
27. Dezember 10 um 14:54 Uhr
Funktioniert nicht für statische Elementfunktionen, schrecklich mehrdeutig für Instanzmethoden, da Datenelemente nicht virtuell sein können. Und der Workaround ist trivial mit einer Überladung.
– Hans Passant
27. Dezember 10 um 16:19 Uhr
@Armen Ich denke, es ist vernünftig zu erwarten, dass es funktioniert, und ich kann keinen sprachlichen Grund erkennen. “Standardargumente müssen zur Kompilierzeit bekannt sein” ist meiner Meinung nach kein Grund. Im obigen Code das Standardargument ist zur Kompilierzeit bekannt – es ist ein Aufruf von .size() der Klasse std::stringdes Mitglieds some_member_variable. Das ist alles, was benötigt wird. Die Überladungsauflösung erfolgt ohne Berücksichtigung von Standardargumenten (andernfalls hätten wir eine zirkuläre Abhängigkeit). Wenn wir also das Standardargument ersetzen, wissen wir, glaube ich, welches Objekt wir berühren müssen.
– Johannes Schaub – litb
15. Juni 11 um 19:11 Uhr
Update: @user396672 liefert einen aufschlussreichen sprachlichen Grund.
– Johannes Schaub – litb
15. Juni 11 um 19:32 Uhr
Nawaz
Ihr Code (vereinfacht):
struct X
{
int mem;
void f(int param = mem); //ERROR
};
Sie möchten nicht statische Elementdaten als Standardwert für einen Parameter einer Elementfunktion verwenden. Die erste Frage, die mir in den Sinn kommt, ist diese: Welche bestimmte Instanz der Klasse den Standardwert mem gehört?
X x1 = {100}; //mem = 100
X x2 = {200}; //mem = 200
x1.f(); //param is 100 or 200? or something else?
Ihre Antwort könnte sein 100 wie f() wird für das Objekt aufgerufen x1 was hat mem = 100. Wenn dies der Fall ist, muss die Implementierung implementiert werden f() wie:
void f(X* this, int param = this->mem);
was wiederum erfordert, dass das erste Argument zuerst initialisiert wird, bevor das andere Argument initialisiert wird. Der C++-Standard legt jedoch keine Initialisierungsreihenfolge der Funktionsargumente fest. Daher ist das nicht erlaubt. Aus dem gleichen Grund, aus dem C ++ Standard nicht einmal dies zulässt:
int f(int a, int b = a); //§8.3.6/9
Tatsächlich heißt es in §8.3.6/9 ausdrücklich:
Standardargumente werden bei jedem Aufruf der Funktion ausgewertet. Die Reihenfolge der Auswertung von Funktionsargumenten ist nicht festgelegt. Folglich dürfen Parameter einer Funktion nicht in Standardargumentausdrücken verwendet werdenauch wenn sie nicht ausgewertet werden.
Und der Rest des Abschnitts ist eine interessante Lektüre.
Ein interessantes Thema im Zusammenhang mit “Standard” -Argumenten (jedoch nicht mit diesem Thema verwandt):
Standardargument in der Mitte der Parameterliste?
Akzeptiert für die ordentliche und logische Erklärung der Antwort von @ user396672
– Armen Tsirunyan
15. Juni 11 um 20:11 Uhr
@Armen: Ich habe einen Link zu einem anderen Thema hinzugefügt. 😀
– Nawaz
15. Juni 11 um 20:12 Uhr
Hat aber nichts mit der Fragestellung zu tun, oder? 🙂
– Armen Tsirunyan
15. Juni 11 um 20:15 Uhr
Wendest du die Implementierung (die nur eine mögliche Implementierung ist) an void f(X* this = self, int param = self->mem);Auswertungsreihenfolge ist nicht wichtig.
– Jarod42
20. Oktober 21 um 10:07 Uhr
Welpe
Standardargumente müssen zur Kompilierzeit bekannt sein. Wenn Sie über so etwas wie einen Funktionsaufruf sprechen, dann ist die Funktion zur Kompilierzeit bekannt, auch wenn der Rückgabewert nicht bekannt ist, sodass der Compiler diesen Code generieren kann, aber wenn Sie standardmäßig eine Member-Variable verwenden, tut der Compiler dies nicht nicht wissen, wo diese Instanz zur Kompilierzeit zu finden ist, was bedeutet, dass sie effektiv einen Parameter übergeben müsste (this) um mich zu finden. Beachten Sie, dass Sie so etwas nicht tun können void func(int i, int f = g(i)); und die beiden sind effektiv die gleiche Einschränkung.
Ich finde diese Einschränkung auch doof. Aber C++ ist voller alberner Beschränkungen.
+1 für die Bemerkung, dass die Frage nicht wirklich klassen- oder gar OO-bezogen ist. Ich nehme jedoch an, dass die Einschränkung eher den Bewertungskontext als die Bewertungszeit betrifft
– Benutzer396672
27. Dezember 10 um 17:22 Uhr
-1 für "Default arguments have to be known at compile-time". Das ist nicht wahr.
– Nawaz
15. Juni 11 um 19:09 Uhr
Hoppla. Ich kann kein Downvote abgeben, da ich bereits im Dezember (2010) mein Upvote abgegeben habe. Da lag ich falsch!
– Nawaz
15. Juni 11 um 19:11 Uhr
Ich denke @user396672 hat mich überzeugt. Es würde erfordern, dass der Objektausdruck vor allen Standardargumenten ausgewertet wird.
– Johannes Schaub – litb
15. Juni 11 um 19:30 Uhr
@Armen: Ohh .. ich habe das noch einmal gelesen, jetzt die vollständige Antwort. Ich stimme der Begründung zu. Dasselbe dachte ich auch. Habe das positiv bewertet.
– Nawaz
15. Juni 11 um 19:36 Uhr
Wie DeadMG oben erwähnt hat, so etwas wie
void func(int i, int f = g(i))
ist aus dem gleichen Grund illegal. Ich vermute jedoch, dass es sich nicht einfach um eine dumme Einschränkung handelt. Um eine solche Konstruktion zu ermöglichen, müssen wir die Auswertungsreihenfolge für Funktionsparameter einschränken (da wir dies vor this->mem berechnen müssen), aber der C++-Standard lehnt explizit alle Annahmen zur Auswertungsreihenfolge ab.
Die akzeptierte Antwort in der doppelten Frage lautet warum, aber der Standard gibt auch ausdrücklich an, warum dies so ist:
8.3.6/9:
” Beispiel: Die Deklaration von X::mem1() im folgenden Beispiel ist falsch formatiert, da für das als Initialisierer verwendete nichtstatische Element X::a kein Objekt bereitgestellt wird.
int b;
class X
int a;
int mem1(int i = a); // error: nonstatic member a
// used as default argument
int mem2(int i = b); // OK: use X::b
static int b;
};
Die Deklaration von X::mem2() ist jedoch sinnvoll, da kein Objekt benötigt wird, um auf das statische Element X::b zuzugreifen. Klassen, Objekte und Mitglieder werden in Abschnitt 9 beschrieben. “
… und da es keine Syntax gibt, um das Objekt bereitzustellen, das zum Auflösen des Werts von erforderlich ist X::a An diesem Punkt ist es praktisch unmöglich, nichtstatische Elementvariablen als Initialisierer für Standardargumente zu verwenden.
ISO C++ Abschnitt 8.3.6/9
ein nicht statisches Element darf nicht in einem Standardargumentausdruck verwendet werdenauch wenn es nicht ausgewertet wird, es sei denn, es erscheint als id-Ausdruck eines Klassenmitglieds-Zugriffsausdrucks (5.2.5) oder es wird verwendet, um einen Zeiger auf ein Mitglied zu bilden (5.3.1).
Sehen Sie sich auch das Beispiel in diesem Abschnitt an.
Nein, Standardparameter sind möglicherweise keine bekannte Kompilierzeit. Beispielsweise ist void f(int x = g()), wobei g eine globale Funktion ist, in Ordnung
– Armen Tsirunyan
27. Dezember 10 um 14:57 Uhr
Jerry Sarg
Aus einem Grund, weil f ist öffentlich, aber mem ist privat. Als solches codieren Sie wie folgt:
int main() {
X x;
x.f();
return 0;
}
… würde externen Code beinhalten, der die privaten Daten von X abruft.
Abgesehen davon würde (oder könnte) es auch die Codegenerierung etwas knifflig machen. Wenn der Compiler ein Standardargument verwendet, erhält er normalerweise den Wert, den er als Teil der Funktionsdeklaration übergeben wird. Das Generieren von Code zum Übergeben dieses Werts als Parameter ist trivial. Wenn Sie möglicherweise ein Element eines Objekts übergeben (möglicherweise willkürlich tief verschachtelt) und dann Dinge wie die Möglichkeit hinzufügen, dass es sich um einen abhängigen Namen in einer Vorlage handelt, kann dies (zum Beispiel) ein anderes Objekt mit einer Konvertierung in das richtige Ziel benennen type, und Sie haben ein Rezept, um die Codegenerierung ziemlich schwierig zu machen. Ich weiß es nicht genau, aber ich vermute, dass jemand über solche Dinge nachgedacht und entschieden hat, dass es besser ist, konservativ zu bleiben, und möglicherweise später ausdünnt, wenn ein triftiger Grund dafür gefunden wurde. Angesichts der vielen Male, die ich gesehen habe, dass daraus Probleme entstehen, würde ich vermuten, dass es lange so bleiben wird, wie es ist, einfach weil es selten Probleme verursacht.
Nein, Standardparameter sind möglicherweise keine bekannte Kompilierzeit. Beispielsweise ist void f(int x = g()), wobei g eine globale Funktion ist, in Ordnung
– Armen Tsirunyan
27. Dezember 10 um 14:57 Uhr
Wladimir
Der Compiler muss Adressen kennen, um die Standardwerte zur Kompilierzeit beizubehalten. Adressen von nicht statischen Mitgliedsvariablen sind zur Kompilierzeit unbekannt.
int add_random(int k, int m=rand()){ return k+ m;} ist zulässig, obwohl weder der Zufallswert noch die Adresse zur Kompilierzeit bekannt sind. Der Enum-Standardwert hat möglicherweise überhaupt keine Adresse.
– Benutzer396672
27. Dezember 2010 um 17:40 Uhr
Bist du sicher? Ich meine, jedes Mal, wenn ich eine ausführbare Datei starte, hat der Parameter m (den Sie wie folgt definiert haben: m = rand() ) auf meinem Computer denselben Wert. Es scheint, als wäre es einmal definiert (zur Kompilierzeit) und ändert den Wert überhaupt nicht. Aufzählungen sind Konstanten – ihre Werte sind zur Kompilierzeit bekannt.
– Wladimir
27. Dezember 10 um 21:52 Uhr
.
7583800cookie-checkNicht statisches Mitglied als Standardargument einer nicht statischen Mitgliedsfunktion [duplicate]yes
@marcog: Obwohl ich zustimmen kann, dass dies etwas verwandt ist, glaube ich, dass dies überhaupt kein Duplikat ist …
– Armen Tsirunyan
27. Dezember 10 um 14:49 Uhr
@Armen Die dort akzeptierte Antwort beantwortet Ihre Frage etwas: Der Compiler kennt die Instanz beim Analysieren des Standardarguments nicht.
– moinudin
27. Dezember 10 um 14:54 Uhr
Funktioniert nicht für statische Elementfunktionen, schrecklich mehrdeutig für Instanzmethoden, da Datenelemente nicht virtuell sein können. Und der Workaround ist trivial mit einer Überladung.
– Hans Passant
27. Dezember 10 um 16:19 Uhr
@Armen Ich denke, es ist vernünftig zu erwarten, dass es funktioniert, und ich kann keinen sprachlichen Grund erkennen. “Standardargumente müssen zur Kompilierzeit bekannt sein” ist meiner Meinung nach kein Grund. Im obigen Code das Standardargument ist zur Kompilierzeit bekannt – es ist ein Aufruf von
.size()
der Klassestd::string
des Mitgliedssome_member_variable
. Das ist alles, was benötigt wird. Die Überladungsauflösung erfolgt ohne Berücksichtigung von Standardargumenten (andernfalls hätten wir eine zirkuläre Abhängigkeit). Wenn wir also das Standardargument ersetzen, wissen wir, glaube ich, welches Objekt wir berühren müssen.– Johannes Schaub – litb
15. Juni 11 um 19:11 Uhr
Update: @user396672 liefert einen aufschlussreichen sprachlichen Grund.
– Johannes Schaub – litb
15. Juni 11 um 19:32 Uhr