Rückgabe über Referenz
-
*lol*
-
meinung schrieb:
*lol*
Sehr konstruktiv, prima.
-
Da wird nix lange erhalten... Das ist ein temporäres Objekt das wird gleich wieder gelöscht noch vor dem verlassen der Funktion.
-
Was Du machen kannst, ist einen Pointer zurueckzuliefern:
anystring* foo() { anystring *x; x = new anystring ("bar); return x; }Nur dann musst auch dafuer sorgen, dass dieser irgentwo spaeter auch wieder deleted wird.
-
MBCS-CITP schrieb:
Was Du machen kannst, ist einen Pointer zurueckzuliefern:
anystring* foo() { anystring *x; x = new anystring ("bar); return x; }Nur dann musst auch dafuer sorgen, dass dieser irgentwo spaeter auch wieder deleted wird.
*lol*
-
Jordy! Das was du machst, ist ein typischer Fehler. Das Objekt auf dem Stack existiert natürlich irgendwie weiter im Speicher, aber es ist ungültig. Und niemand kann dir versichern, das die Daten an der Speicherstelle nicht im nächsten Moment mit was anderem überschrieben wird.
int h[2] = {0,1}; std::cout << h[2];DAS geht auch, nur ist im dritten Feld wohl einfach etwas, was für dich ungültig ist. Klar steht da irgend ein Wert drin, nur gültig ist der trotzdem nicht.
Genau das gleich mit den temporären Variablen. Das hat auch nichts mit neuen oder alten GCC zu tun. Beim neuen ist es wie beim alten, nur _warnt_ dich der neue halt, was der alte einfach verseumt hatte.
Wenn du temp. Objekte zurück geben willst, benutz std::auto_ptr!!! Schau dazu auf meine Homepage, hab ich was zu geschrieben.
-
Wenn du temp. Objekte zurück geben willst, benutz std::auto_ptr!!!
*lol*
-
Jordy schrieb:
meinung schrieb:
*lol*
Sehr konstruktiv, prima.
Auf (unsinnige) Beiträge von Unregs sollte man einfach nicht reagieren, EGAL was darin steht. Hab mich schon damit abgefunden und sicherlich alle anderen alten Hasen hier auch. Solltest du dir auch angewöhnen, einfach NICHT zu reagieren.

-
doch

-
auto_ptr würd ich aber auch nicht favorisieren.
hier in genau diesem Fall bietet sich sogar eine tolle Möglichkeit an:
const anystring& foo() { static anystring str("bar"); return str }aber: das ist kein allheilmittel da foo immer dasselbe Objekt zurückliefert. Wird str irgendwie verändert wirkt sich diese veränderung global aus.
-
Wäre super, wenn du noch sagen könntest, warum du auto_ptr nicht favorisierst, obwohl du keine Alternative in der Schublade hast.
-
ähm ich hab doch ne alternative in der Schublade

1. auto_ptr ist hier schlecht, weil er einfach nicht anständig kopiert werden kann. Es besteht hier kein Grund dafür, zu verhindern dass mehrere Zeiger auf den String zeigen. Steht dir natürlich frei, das Gegenteil zu beweisen. Bis dahin ist boost::shared_ptr meine Wahl.
2. es wird nach ner Referenz gefragt, und die bietest pointer als Lösungsmöglichkeit an.
-
und wieso nicht einfach ein anystring zurückgeben ? wenn das kopieren ein problem ist, löst man das mit move-konstruktoren und nicht mit pointern - immerhin geht das mit g++ ganz gut.
-
Erstmal danke für die Diskussion.
Dann bezüglich der unregistrierten Usern: Ich werde es mir zu Herzen nehmen und sie ignorieren. Schade, daß es unregistrierten erlaubt ist überhaupt zu posten. Das ist in den meisten Foren, die ich kenne, nicht so, was wohl auch der Grund ist, warum ich nicht gewöhnt bin, so behandelt zu werden.
Dann noch zu dem Unsinn den ich geschrieben habe. Ich habe nochmal nachgesehen. Natürlich habt ihr recht: Das funktioniert nicht und hat nie funktioniert. Ich habe das verwechselt. Aber egal. Um so länger ich nachggedacht habe, um so verwunderter bin ich, *daß* es *nicht* funktioniert. Ich versuche hinter meinen Denkfehler zu kommen. Dazu zwei Beispiele.
Hier nochmal mein Unsinn

const typ& funktion1() { return typ(10); } int main() { const typ& x = funktion1(); }Ich habe mir gesagt, daß das temporäre Objekt innerhalb der Funktion gelöscht wird (das ist schließlich Tatsache) weil es nur innerhalb des Klassen-Scope exisitert. In der folgenden, so weit ich weiß korrekten Funktion, wird ebenfalls innerhalb der Funktion ein temporäres Objekt erzeugt, das außerhalb des Funktionsaurufes weiter existiert (sonst könnte es nicht als Rückgabewert dienen):
const typ funktion2() { return typ(10); }Das ist also irgendwie nicht das schlagende Argument, denn offenbar kann ein temporäres Objekt doch weiterexistieren.
Dann habe ich mir gesagt, daß das temporäre Objekt, wenn es über eine Referenz zurückgegeben werden soll, ja erst einer Art temporären Referenz zugewiesen wird, bevor es dann der endgültigen Referenz (im Beispiel "x") zugewiesen wird. Aber auch das ist kein Problem, denn ein temporäres Objekt existiert solange, wie die Referenz, die auf es verweist. Zumindest ist das im folgenden Fall so:void main() { const typ &y = funktion2(); } // das temporäre objekt von funktion2() wird erst hier vernichtetAlso, wo ist mein Denkfehler? Wie hätte ich mir herleiten können, daß die funktion1() Blödsinn ist, ohne den speziellen Fall zu kennen oder auszuprobieren?
Achja, wo ich gerade dabei bin mich zu blamieren...

Ich habe noch so einen Denkfehler, den mir offenbar kein Buch erklären kann. Folgendes:class a { public: a(int x) : member(x) {} int member; }; void main() { a var2(10); // Fall 1 a var1 = 10; // Fall 2 }Das funktioniert einwandfrei. In beiden Fällen wird völlig korrekt der int-Konstruktor aufgerufen. Ich habe auch extra noch einige Tests mit überladenen copy-Konstruktoren und Zuweisungsoperatoren gemacht, um sicher zu sein, daß ich keinen Denkfehler mache und keinerlei implizite Aufrufe stattfinden. Die beiden Fälle sind hier offenbar völlig äquivalent (das war es zumindest, was ich immer dachte).
Nun habe ich folgendes gemacht:
class a { public: explicit a(int x) : member(x) {} int member; }; void main() { a var1(10); // Fall 1 a var2 = 10; // Fall 2 -> Fehler }Jetzt knallt es. Um es vorweg zu nehmen: daß hier ein Fehler auftreten würde, war mir völlig klar.

Nur das *Warum* nicht. Und alle Quellen die ich prüfen konnte, haben sich darüber ausgeschweigen.
"Plötzlich" behauptet der Compiler, was vorher völlig korrekt funktioniert hat, wäre jetzt illegal. Er sagt sogar, daß der Fall 2 eine implizite Konvertierung beinhalten würde. Und genau das verstehe ich nicht. Wenn der Compiler im ersten Fall offenbar keine impliziten Konvertierungen gemacht hat, warum sollte er (*mit* dem explicit Schlüsselwort) plötzlich doch eine brauchen? Immerhin soll 'explicit' implizite Konvertierungen verbieten, aber doch nicht *erzeugen*?!
Das gleiche passiert bei Verwendung eines Auto-Pointers (dessen Konstruktoren ja auch explizit sind) und einer Zuweisung von 'new':
class a { public: explicit a(int x) : member(x) {} int member; }; void main() { auto_ptr<a> var1 = new a(10); // Fall 1 -> Fehler auto_ptr<a> var2(new a(10)); // Fall 2 }Auch hier wußte ich, daß es einen Unterschied gibt, konnte aber nirgends eine Erklärung finden, *warum* die beiden Fälle sich unterschieden.
Mögen jetzt manche sagen: Ist doch egal, Hauptsache man weiß *wie* es geht, aber mich wurmt das.

-
Jordy schrieb:
Hier nochmal mein Unsinn

const typ& funktion1() { return typ(10); } int main() { const typ& x = funktion1(); }Ich habe mir gesagt, daß das temporäre Objekt innerhalb der Funktion gelöscht wird (das ist schließlich Tatsache) weil es nur innerhalb des Klassen-Scope exisitert. In der folgenden, so weit ich weiß korrekten Funktion, wird ebenfalls innerhalb der Funktion ein temporäres Objekt erzeugt, das außerhalb des Funktionsaurufes weiter existiert (sonst könnte es nicht als Rückgabewert dienen):
const typ funktion2() { return typ(10); }In der ersten Version gibst du eine Referenz auf das lokale Objekt zurück. In der zweiten Version wird ein *Kopie* des lokalen Objekts erzeugt. Das sind zwei völlig verschiedene Paar Schuhe.
Jordy schrieb:
Ich habe noch so einen Denkfehler, den mir offenbar kein Buch erklären kann. Folgendes:
class a { public: a(int x) : member(x) {} int member; }; void main() { a var2(10); // Fall 1 a var1 = 10; // Fall 2 }Das funktioniert einwandfrei. In beiden Fällen wird völlig korrekt der int-Konstruktor aufgerufen. Ich habe auch extra noch einige Tests mit überladenen copy-Konstruktoren und Zuweisungsoperatoren gemacht, um sicher zu sein, daß ich keinen Denkfehler mache und keinerlei implizite Aufrufe stattfinden. Die beiden Fälle sind hier offenbar völlig äquivalent (das war es zumindest, was ich immer dachte).
Äquivalent im Verhalten, aber nicht identisch.
Den Unterschied habe ich hier erklärt: http://fara.cs.uni-potsdam.de/~kaufmann/?page=GenCppFaqs&faq=copyvsdirect#AnswJordy schrieb:
Nun habe ich folgendes gemacht:
class a { public: explicit a(int x) : member(x) {} int member; }; void main() { a var1(10); // Fall 1 a var2 = 10; // Fall 2 -> Fehler }Jetzt knallt es. Um es vorweg zu nehmen: daß hier ein Fehler auftreten würde, war mir völlig klar.

Nur das *Warum* nicht.Weil das die Bedeutung von explicit ist.
Im zweiten Fall passiert (theoretisch) folgendes:
1. Aus dem Integer 10 wird in ein temporäres X-Objekt erzeugt (implizite Konvertierung von int nach a).
2. Das temporäre X-Objekt wird über den Copy-Ctor für die Erzeugung von var2 verwendet.Erstens erfordert eine implizite Konvertierung von a nach int. Für eine solche implizite Konvertierung kommen zwei Konstrukte in Frage. Entweder ein Konstruktor der sich mit einem Argument aufrufen lässt oder aber ein Konvertierungsoperator. Letzterer fällt weg, da int keine Klasse ist und somit keinen Konvertierungsoperator nach a haben kann. Bleibt der Ein-Parameter-Konstruktor.
Ein solcher wird aber nur für implizite Konvertierungen verwendet, wenn er *nicht* als explicit deklariert wurde. explicit sorgt also dafür, dass ein so deklarierter Konstruktor niemals für implizite Konvertierungen verwendet wird.
-
HumeSikkins schrieb:
In der ersten Version gibst du eine Referenz auf das lokale Objekt zurück. In der zweiten Version wird ein *Kopie* des lokalen Objekts erzeugt. Das sind zwei völlig verschiedene Paar Schuhe.
Hm. Wenn dem so ist (das hatte ich auch mal vermutet) dann tut das aber wohl keiner Compiler in der Realität. Auch nicht unter Deaktivierung aller Optimierungen. Jedenfalls - auch wenn es kein Compiler in der Realität macht - ist das natürlich die Erklärung.
HumeSikkins schrieb:
Äquivalent im Verhalten, aber nicht identisch.
Den Unterschied habe ich hier erklärt: http://fara.cs.uni-potsdam.de/~kaufmann/?page=GenCppFaqs&faq=copyvsdirect#AnswHumeSikkins schrieb:
Weil das die Bedeutung von explicit ist.
Im zweiten Fall passiert (theoretisch) folgendes:
1. Aus dem Integer 10 wird in ein temporäres X-Objekt erzeugt (implizite Konvertierung von int nach a).
2. Das temporäre X-Objekt wird über den Copy-Ctor für die Erzeugung von var2 verwendet.Ahja. Dann wird dieser Fall in diversen Beschreibungen aber mächtig vereinfacht. Ich habe nämlich mehrfach gelesen, daß beide Fälle einem Aufruf des Konstruktors auf den rechten Ausdruck entsprechen. Der Copy-Konstruktor wurde nicht erwähnt. Da auch Compiler genau diesen Code produzieren, habe ich das nicht in Frage gestellt. Auch hier läuft bei den Compilern wohl eine Art von Optimierung, die sich nicht abstellen läßt. Jedenfalls ist mir jetzt auch der Rest klar.
Trotzdem frage ich mich, warum der Sprachstandard das so definiert. Immerhin hätte man doch auch problemlos sagen können, daß auch eine Initalisierung mit '=' grundsätzlich den passenden Konstruktor aufruft (falls vorhanden), also wirklich *identisch* ist. Das hätte doch genau so funktioniert und die leidige Einschränkung, die '=' Syntax bei expliciten Konstruktoren nicht verwenden zu können, wäre weggefallen, oder? Und der Copy-Konstruktor-Fall wäre wirklich nur noch dann zum Tragen gekommen, wenn schon ein Objekt des gleichen Typs vorliegt.
Achja, danke für deine erfreulich sachlichen Erläuterungen, das hat nicht jeder hier geschafft

-
Jordy schrieb:
HumeSikkins schrieb:
In der ersten Version gibst du eine Referenz auf das lokale Objekt zurück. In der zweiten Version wird ein *Kopie* des lokalen Objekts erzeugt. Das sind zwei völlig verschiedene Paar Schuhe.
Hm. Wenn dem so ist (das hatte ich auch mal vermutet) dann tut das aber wohl keiner Compiler in der Realität. Auch nicht unter Deaktivierung aller Optimierungen. Jedenfalls - auch wenn es kein Compiler in der Realität macht - ist das natürlich die Erklärung.
Die Optimierung die hier durchgeführt wird, nennt sich Return-Value-Optimization (RVO). Der Punkt ist auch hier, dass der Copy-Ctor zugreifbar sein muss, selbst wenn der Compiler den eigentlichen Kopiervorgang wegoptimiert.
Der folgende Code ist z.B. illegal, nichtsdestotrotz wird er von einigen Compilern übersetzt:
class Typ { public: Typ(int); private // Achtung: Copy-Ctor ist private // Typ kann niemals by-value returned werden Typ(const Typ&); }; const Typ func() { return Typ(10); // Illegal, selbst wenn Compiler // die Kopie eleminiert. }
-
HumeSikkins schrieb:
Jordy schrieb:
HumeSikkins schrieb:
In der ersten Version gibst du eine Referenz auf das lokale Objekt zurück. In der zweiten Version wird ein *Kopie* des lokalen Objekts erzeugt. Das sind zwei völlig verschiedene Paar Schuhe.
Hm. Wenn dem so ist (das hatte ich auch mal vermutet) dann tut das aber wohl keiner Compiler in der Realität. Auch nicht unter Deaktivierung aller Optimierungen. Jedenfalls - auch wenn es kein Compiler in der Realität macht - ist das natürlich die Erklärung.
Die Optimierung die hier durchgeführt wird, nennt sich Return-Value-Optimization (RVO). Der Punkt ist auch hier, dass der Copy-Ctor zugreifbar sein muss, selbst wenn der Compiler den eigentlichen Kopiervorgang wegoptimiert.
Der folgende Code ist z.B. illegal, nichtsdestotrotz wird er von einigen Compilern übersetzt:
class Typ { public: Typ(int); private // Achtung: Copy-Ctor ist private // Typ kann niemals by-value returned werden Typ(const Typ&); }; const Typ func() { return Typ(10); // Illegal, selbst wenn Compiler // die Kopie eleminiert. }Ok, danke. Ist jetzt alles klar.
Und zudem habe ich gelernt, mich mit Sicherheit nicht mehr auf das Verhalten von konkreten Compiler zu stützen. Naja, eigentlich eine Weisheit die man kennt...